﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Collections;
using System.Data.SqlTypes;
using System.Data.SqlClient;

using Noris.Schedule.Support;
using Noris.Schedule.Support.Data;
using Noris.Schedule.Support.Sql;
using Noris.Schedule.Support.DataFace;
using Noris.Schedule.Support.Green;
using Noris.Schedule.Support.Core;

using Noris.Schedule.Planning.DataFace;
using Noris.Schedule.Planning.ProcessData.Time;
using System.Data;

namespace Noris.Schedule.Planning.ProcessData
{
    #region CLASS StockCardCls : jedna karta na ose S (skladová karta a její časový vývoj)
    /// <summary>
    /// Třída, která obsahuje vývoj zásob jedné plánovací jednotky na jednom skladu.
    /// Jeden záznam odpovídá jedné skladové kartě ve výrobě (zdroj = plánovací jednotka S osy, sklad = sklad MRP).
    /// Data v objektu: výchozí zásoba, minimální zásoba, vývoj stavu zásoby v čase.
    /// Postup vývoje zásoby je prezentován seznamem změn, což jsou záznamy třídy StockChangeCls.
    /// Záznamy StockChangeCls jsou udržovány časově setříděné, lze tedy snadno určit zásobu v libovolný okamžik.
    /// </summary>
    public class StockCardCls : IRecordKey, IDisposable
    {
        #region KONSTRUKCE, TVORBA OBJEKTU SKLADOVÉ KARTY Z DODANÝCH OBJEKTŮ
        /// <summary>
        /// Konstruktor pro konkrétní stav skladu a plánovací jednotku/hladinu LLC.
        /// Interně přiděluje číslo StockCardId.
        /// </summary>
        /// <param name="planUnitSSl">Data skladové karty = stav skladu plánovací jednotky</param>
        /// <param name="kmenovaKartaSkladu">Kmenová karta skladu</param>
        /// <param name="stavSk">Stav skladu</param>
        /// <param name="skSklad">Data skladu</param>
        /// <param name="planUnitS">Data plánovací jednotky</param>
        public StockCardCls(PlanUnitSSlCls planUnitSSl, KmenovaKartaSkladuCls kmenovaKartaSkladu, StavSkCls stavSk, SkSkladCls skSklad, PlanUnitSCls planUnitS)
        {
            this._Init(planUnitSSl, kmenovaKartaSkladu, stavSk, skSklad, planUnitS);
        }
        /// <summary> (static)
        /// ID pro vytvoření klíče příští nové skladové karty
        /// </summary>
        private static int _NextID;
        /// <summary>
        /// Iniciace objektu výrobní skladové karty
        /// </summary>
        /// <param name="planUnitSSl">Data skladové karty = stav skladu plánovací jednotky</param>
        /// <param name="kmenovaKartaSkladu">Kmenová karta skladu</param>
        /// <param name="stavSk">Stav skladu</param>
        /// <param name="skSklad">Data skladu</param>
        /// <param name="planUnitS">Data plánovací jednotky</param>
        private void _Init(PlanUnitSSlCls planUnitSSl, KmenovaKartaSkladuCls kmenovaKartaSkladu, StavSkCls stavSk, SkSkladCls skSklad, PlanUnitSCls planUnitS)
        {
            if (stavSk == null)
                Throw.SysError(MessageInfo.Get("Nelze vytvořit kartu skladu bez předání dat o skladové kartě modulu Sklad."));

            if (planUnitSSl == null)
                Throw.SysError(MessageInfo.Get("Nelze vytvořit kartu skladu bez předání dat o skladové kartě modulu Výroba."));

            if (planUnitS == null)
                Throw.SysError(MessageInfo.Get("Nelze vytvořit kartu skladu bez předání dat o plánovací jednotce S zdrojů."));

            this.StockCardId = ++_NextID;
            this._StockChanges = new List<StockChangeCls>();
            this.StavSk = stavSk;

            // Nulovat stavy skladu na výrobní kartě:
            planUnitSSl.QtyMfrClear();
            this.PlanUnitSData = planUnitS;
            this.PlanUnitSSl = planUnitSSl;
            this.StockUnit = new StockUnit(planUnitSSl.Stock, planUnitSSl.PlanUnitS);
            this.InitialStockQty = 0M;
            _MinStockQty = 0.0M;

            this.NeedSorting = false;
            this.IsChanged = false;
            this.StockCardState = StockCardStateType.Initialized;
            this.IsPlanning = (kmenovaKartaSkladu.Planovat == "A" && skSklad != null && skSklad.Planovat == "A");
            this.CumulationMode = ConvertStringToStockCardCumulationMode((kmenovaKartaSkladu == null) ? "" : kmenovaKartaSkladu.KumulaceNavrhy);
        }
        /// <summary>Příznak, že nedostatečná výchozí zásoba je spolehlivě vyřešena existujícími příjmy na začátku osy změn</summary>
        private bool Xxx_InitialMinQtyFixed;
        /// <summary>Index první položky _StockChanges, kde je možno najít příjem, který ještě nebyl posunutý ToHistory při tvorbě zajištění.</summary>
        private int _FirstReceiptForTransfer;
        /// <summary>Minimální zásoba, načtená ze stavu skladu</summary>
        private decimal _MinStockQty;
        /// <summary>Soupis změn na skladové kartě</summary>
        private List<StockChangeCls> _StockChanges;
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "{SU:" + StockUnit.ToString() + "; InitialQty=" + InitialStockQty.ToString() + "; ChangeCnt=" + _StockChanges.Count.ToString() + "}";
        }
        #endregion
        #region PUBLIC PROPERTY
        /// <summary> { get; } 
        /// ID této skladové karty, jednoznačný integer
        /// </summary>
        public int StockCardId { get; private set; }
        /// <summary> { get; } 
        /// Klíč skladu a plánovací jednotky = uživatelský klíč, podle něj se skladová karta hledá
        /// </summary>
        public StockUnit StockUnit { get; private set; }
        /// <summary> { get; } 
        /// Číslo hladiny LLC, určuje se při zakládání skladové karty, načte se z odpovídající planUnitS, slouží k pdržbě indexů podle skladu a LLC
        /// </summary>
        public int PlanUnitSLlc { get { return this.PlanUnitSData.Llc; } }
        /// <summary>
        /// Soupis změn na skladové kartě. 
        /// Pozor: { get; } vyvolá metodu SortIfNeed(); = zajistí vrácení korektních dat.
        /// </summary>
        internal List<StockChangeCls> StockChanges { get { this.CheckChanges(); return _StockChanges; } }
        /// <summary> { get; }
        /// Stav této skladové karty v procesu plánování.
        /// Stav lze nastavit metodou SetStockCardState().
        /// </summary>
        internal StockCardStateType StockCardState { get; private set; }
        /// <summary> { get; }
        /// Stav existence vztahů typu AxisBalance pro tuto skladovou kartu.
        /// Jde o odhadovaný stav: po spuštění metody GetAxisBalance() se nastaví na true 
        /// (metoda vrací soupis vztahů, ale nezajišťuje jejich skutečné vložení do AxisHeap = nemá do toho modulu přístup).
        /// Po spuštění GetAxisGIdForRemoveBalance() se nastaví na false
        /// (metoda vrací soupis GID záznamů AxisS, ale nezajišťuje pro ně smazání vztahů).
        /// </summary>
        internal bool AxisBalanceExists { get; private set; }
        /// <summary> { get; } 
        /// Záznam plánovací jednotky S zdrojů
        /// </summary>
        internal PlanUnitSCls PlanUnitSData { get; private set; }
        /// <summary> { get; } 
        /// Záznam stavu skladu plánovací jednotky
        /// </summary>
        internal PlanUnitSSlCls PlanUnitSSl { get; private set; }
        /// <summary> { get; } 
        /// Data stavu skladu (Warehouse), pro sklad = MRP sklad a kmenovou kartu plánovací jednotky.
        /// </summary>
        internal StavSkCls StavSk { get; private set; }
        ///<summary> { get; } 
        ///Režim množstevní kumulace návrhů
        ///</summary>
        internal StockCardCumulationMode CumulationMode { get; private set; }
        /// <summary> { get; }
        /// Výchozí disponibilní zásoba na stavu skladu (sumárně ze všech skladů podřízených jednomu MRP skladu).
        /// Je napočtena ze stavů skladů, ale jen pokud je povoleno v parametrech MRP (MrpAcceptStockFund == true).
        /// Od této hodnoty se vychází při zajišťování materiálu v MPR.
        /// Liší se od hodnoty this.SlQtyDisp právě tou podmínkou (MrpAcceptStockFund == true). 
        /// this.SlQtyDisp obsahuje vždy disponibilní výchozí zásobu, kdežto this.InitialStockQty ji obsahuje jen když je povoleno s ní pracovat.
        /// </summary>
        public decimal InitialStockQty { get; private set; }
        /// <summary> { get; }
        /// Minimální zásoba (sumárně ze všech skladů podřízených jednomu MRP skladu).
        /// Typicky jde o zásobu na konci plánování.
        /// Pokud by nějakou chybou bylo určeno záporné číslo, pak je zde uvedena 0.
        /// </summary>
        public decimal MinStockQty { get { return (this._MinStockQty >= 0M ? this._MinStockQty : 0M); } }
        /// <summary> { get; }
        /// Příznak, že tato karta potřebuje setřídit svůj interní seznam položek (byla v něm provedena změna s potlačením online třídění)
        /// </summary>
        public bool NeedSorting { get; private set; }
        /// <summary>
        /// Datum první změny v seznamu změn stavu (Items[0].ChangeDate), anebo MinValue pokud nejsou žádné změny
        /// </summary>
        public DateTime FirstChangeDateTime { get { this.CheckChanges(); return (this._StockChanges.Count == 0 ? DateTime.MinValue : this._StockChanges[0].ChangeDate); } }
        /// <summary>
        /// Seznam změn stavu = jednotlivé pohyby na skladové kartě
        /// </summary>
        internal List<StockChangeCls> Items { get { this.CheckChanges(); return this._StockChanges; } }
        ///<summary>Příznak, že tato karta obsahuje změny v disponibilní zásobě, a je třeba ji přepočítat</summary>
        public bool IsChanged { get; private set; }
        ///<summary><para>Atribut: Úroveň [editgeneral]</para><para>Db: lcs.plan_unit_s_axis.level (int not null)</para></summary>
        public int Level { get; internal set; }
        /// <summary>
        /// Příznak, že tato skladová karta se má plánovat (tj. zajistit minimální zásobu a zajistit návrhy na příjem)
        /// false = neplánovat, je zde pouze pro evidenci příjmu z výrobních příkazů, a scheduling kapacit.
        /// </summary>
        public bool IsPlanning { get; private set; }
        #endregion
        #region PUBLIC PROPERTY VÁZANÉ ZE STAVU SKLADU Z MODULU SKLAD (WAREHOUSE)
        ///<summary><para>Vztah 8670: Aktuální dodavatel (zprava: Stav skladu)</para><para>Db: lcs.stav_sk.aktualni_dodavatel (int null)</para></summary>
        public SqlInt32 AktualniDodavatel { get { return this.StavSk.AktualniDodavatel; } }
        ///<summary> { get; }<para>Atribut: Stav skladu.Minimum od dodavatele [editnumeric_19_6]</para><para>Db: lcs.stav_sk.minimum_dodavatel (numeric_19_6 not null)</para></summary>
        public decimal StavSkMinimumDodavatel { get { return this.StavSk.MinimumDodavatel; } }
        ///<summary> { get; }<para>Atribut: Stav skladu.Maximální dávka [editnumeric_19_6]</para><para>Db: lcs.stav_sk.maximalni_davka (numeric_19_6 null)</para></summary>
        public SqlDecimal StavSkMaximalniDavka { get { return this.StavSk.MaximalniDavka; } }
        ///<summary> { get; }Atribut: Stav skladu.Dodávka (počet dní)</summary>
        public int StavSkDodavka { get { return this.StavSk.Dodavka; } }
        ///<summary> { get; }Atribut: Rezerva pořizovací lhůty</summary>
        public int StavSkRezerva { get { return this.StavSk.Rezerva; } }
        ///<summary> { get; }<para>Atribut: Stav skladu.Dělitel příjem [mfr_divider]</para><para>Db: lcs.stav_sk.delitel_prijem (decimal (5) not null)</para></summary>
        public decimal StavSkDelitelPrijem { get { return this.StavSk.DelitelPrijem; } }
        #endregion
        #region PUBLIC PROPERTY VÁZANÉ ZE STAVU SKLADU PLÁNOVACÍ JEDNOTKY
        ///<summary><para>Stav skladu PJ, Atribut: Počet [editmaskmena]</para><para>Db: lcs.plan_unit_s_sl.qty (numeric_19_6 not null)</para></summary>
        public decimal SlQty { get { return this.PlanUnitSSl.Qty; } set { this.PlanUnitSSl.Qty = value; } }
        ///<summary><para>Stav skladu PJ, Atribut: Počet disponibilní [editmaskmena]</para><para>Db: lcs.plan_unit_s_sl.qty_disp (numeric_19_6 not null)</para></summary>
        public decimal SlQtyDisp { get { return this.PlanUnitSSl.QtyDisp; } set { this.PlanUnitSSl.QtyDisp = value; } }
        ///<summary><para>Stav skladu PJ, Atribut: Počet konečný [editmaskmena]</para><para>Db: lcs.plan_unit_s_sl.qty_final (numeric_19_6 not null)</para></summary>
        public decimal SlQtyFinal { get { return this.PlanUnitSSl.QtyFinal; } set { this.PlanUnitSSl.QtyFinal = value; } }
        ///<summary><para>Stav skladu PJ, Atribut: Počet disponibilní konečný [editmaskmena]</para><para>Db: lcs.plan_unit_s_sl.qty_final_disp (numeric_19_6 not null)</para></summary>
        public decimal SlQtyFinalDisp { get { return this.PlanUnitSSl.QtyFinalDisp; } set { this.PlanUnitSSl.QtyFinalDisp = value; } }
        ///<summary><para>Stav skladu PJ, Atribut: Poptáváno [editmaskmena]</para><para>Db: lcs.plan_unit_s_sl.qty_dem (numeric_19_6 not null)</para></summary>
        public decimal SlQtyDem { get { return this.PlanUnitSSl.QtyDem; } set { this.PlanUnitSSl.QtyDem = value; } }
        ///<summary><para>Stav skladu PJ, Atribut: Rezervováno [editmaskmena]</para><para>Db: lcs.plan_unit_s_sl.qty_res (numeric_19_6 not null)</para></summary>
        public decimal SlQtyRes { get { return this.PlanUnitSSl.QtyRes; } set { this.PlanUnitSSl.QtyRes = value; } }
        ///<summary><para>Stav skladu PJ, Atribut: Objednáno [editmaskmena]</para><para>Db: lcs.plan_unit_s_sl.qty_ord (numeric_19_6 not null)</para></summary>
        public decimal SlQtyOrd { get { return this.PlanUnitSSl.QtyOrd; } set { this.PlanUnitSSl.QtyOrd = value; } }
        ///<summary><para>Stav skladu PJ, Atribut: Potvrzeně objednáno [editmaskmena]</para><para>Db: lcs.plan_unit_s_sl.qty_ord_conf (numeric_19_6 not null)</para></summary>
        public decimal SlQtyOrdConf { get { return this.PlanUnitSSl.QtyOrdConf; } set { this.PlanUnitSSl.QtyOrdConf = value; } }
        ///<summary><para>Stav skladu PJ, Atribut: Objednáno ve výrobě [editmaskmena]</para><para>Db: lcs.plan_unit_s_sl.qty_ord_in_prod (numeric_19_6 not null)</para></summary>
        public decimal SlQtyOrdInProd { get { return this.PlanUnitSSl.QtyOrdInProd; } set { this.PlanUnitSSl.QtyOrdInProd = value; } }
        ///<summary><para>Stav skladu PJ, Atribut: Objednáno výrobou [editmaskmena]</para><para>Db: lcs.plan_unit_s_sl.qty_ord_fr_prod (numeric_19_6 not null)</para></summary>
        public decimal SlQtyOrdFrProd { get { return this.PlanUnitSSl.QtyOrdFrProd; } set { this.PlanUnitSSl.QtyOrdFrProd = value; } }
        ///<summary><para>Stav skladu PJ, Atribut: Počet navrženo příjem [editmaskmena]</para><para>Db: lcs.plan_unit_s_sl.qty_prop_in (numeric_19_6 not null)</para></summary>
        public decimal SlQtyPropIn { get { return this.PlanUnitSSl.QtyPropIn; } set { this.PlanUnitSSl.QtyPropIn = value; } }
        ///<summary><para>Stav skladu PJ, Atribut: Počet navrženo výdej [editmaskmena]</para><para>Db: lcs.plan_unit_s_sl.qty_prop_out (numeric_19_6 not null)</para></summary>
        public decimal SlQtyPropOut { get { return this.PlanUnitSSl.QtyPropOut; } set { this.PlanUnitSSl.QtyPropOut = value; } }
        ///<summary><para>Stav skladu PJ, Atribut: Počet obchodní plán [editmaskmena]</para><para>Db: lcs.plan_unit_s_sl.qty_business_plan (numeric_19_6 not null)</para></summary>
        public decimal SlQtyBusinessPlan { get { return this.PlanUnitSSl.QtyBusinessPlan; } set { this.PlanUnitSSl.QtyBusinessPlan = value; } }
        ///<summary><para>Stav skladu PJ, Atribut: Datum prvního návrhu [Datum_cas]</para><para>Db: lcs.plan_unit_s_sl.date_first_prop (datetime null)</para></summary>
        public SqlDateTime SlDateFirstProp { get { return this.PlanUnitSSl.DateFirstProp; } set { this.PlanUnitSSl.DateFirstProp = value; } }
        #endregion
        #region PUBLIC METODY
        /// <summary>
        /// Smaže pohyby na této skladové kartě
        /// </summary>
        internal void ClearPlanData()
        {
            this._StockChanges = new List<StockChangeCls>();
            this.NeedSorting = false;
            this.IsChanged = true;
            this.StockCardState = StockCardStateType.Initialized;
            this.AxisBalanceExists = false;
        }
        /// <summary>
        /// Pro tuto kartu (sklad MRP a danou plánovací jednotku) zvýší výchozí skladovou zásobu a minimální povinnou zásobu.
        /// </summary>
        /// <param name="sumItem">Data načtená ze stavu skladu</param>
        /// <param name="mrpAcceptStockFund">Akceptovat disponibilní zásobu ze stavu skladu jako výchozí pro plánování</param>
        internal void AddInitialStockSummary(StavSkSummaryCls sumItem, bool mrpAcceptStockFund)
        {
            // Podle druhu záznamu:
            switch (sumItem.RecordType)
            {
                case 1:
                case 2:
                    // Výchozí množství ze stavu skladu / z rozpadu:
                    if (mrpAcceptStockFund && sumItem.PocetDisp != 0M)  // Pokud je požadováno, a je skutečně změna výchozího množství:
                    {
                        this.InitialStockQty += sumItem.PocetDisp;
                        this.NeedSorting = true;       //  bude třeba kartu přepočítat. Teď to ale ještě není třeba...
                        this.IsChanged = true;
                        this.StockCardState = StockCardStateType.Initialized;
                    }

                    // Změny, které vepíšu do interního objektu stavu skladu PJ:
                    this.SlQty += sumItem.PocetStav;
                    this.SlQtyDisp += sumItem.PocetDisp;
                    this.SlQtyFinal += sumItem.PocetStav;
                    this.SlQtyFinalDisp += sumItem.PocetDisp;
                    this.SlQtyDem += sumItem.PocetPoptavano;
                    this.SlQtyRes += sumItem.PocetRezervovano;
                    this.SlQtyOrd += sumItem.PocetObjednano;
                    this.SlQtyOrdConf += sumItem.PocetObjednanoPotvrzeno;
                    this.SlQtyOrdInProd += sumItem.PocetObjednanoVyroba;
                    this.SlQtyOrdFrProd += sumItem.PocetPoptavanoVyroba;
                    this.SlQtyPropIn = 0M;
                    this.SlQtyPropOut = 0M;
                    this.SlQtyBusinessPlan = 0M;
                    this.SlDateFirstProp = SqlDateTime.Null;

                    break;
                case 3:
                    // Minimální zásoba:
                    if (!sumItem.PocetMinimum.IsNull && sumItem.PocetMinimum.Value != 0M)
                        this._MinStockQty += sumItem.PocetMinimum.Value;

                    break;
            }
        }
        /// <summary>
        /// Pro tuto kartu (sklad MRP a danou plánovací jednotku) zvýší výchozí skladovou zásobu a minimální povinnou zásobu.
        /// Tato větev se používá již jen pro testy.
        /// </summary>
        /// <param name="initialQty">Výchozí disponibilní množství</param>
        /// <param name="minQty">Minimální povinná zásoba</param>
        internal void AddInitialStockQty(decimal initialQty, decimal minQty)
        {
            this.InitialStockQty += initialQty;
            this._MinStockQty += minQty;

            if (initialQty != 0M)               // Pokud je skutečně změna výchozího množství:
            {
                this.NeedSorting = true;       //  bude třeba kartu přepočítat. Teď to ale ještě není třeba...
                this.IsChanged = true;
                this.StockCardState = StockCardStateType.Initialized;
            }
        }
        /// <summary>
        /// Vynuluje výchozí a minimální zásobu na této kartě
        /// </summary>
        /// <param name="clearInitialQty"></param>
        /// <param name="clearMinQty"></param>
        internal void ClearInitialMinQty(bool clearInitialQty, bool clearMinQty)
        {
            if (clearInitialQty)
                this.InitialStockQty = 0M;

            if (clearMinQty)
                this._MinStockQty = 0M;

            if (clearInitialQty || clearMinQty)
            {
                this.NeedSorting = true;       //  bude třeba kartu přepočítat. Teď to ale ještě není třeba...
                this.IsChanged = true;
                this.StockCardState = StockCardStateType.Initialized;
            }
        }
        /// <summary>
        /// Pro tuto kartu (sklad MRP a danou plánovací jednotku) založí nový záznam o změně množství
        /// </summary>
        /// <param name="axisItem">Záznam osy S o pohybu, pro něj (a s ním) se vytvoří záznam o změně stavu</param>
        internal void AddChangeStockQty(MaterialPlanAxisItemCls axisItem)
        {
            StockCardAddResult addResult;
            _AddChangeStockQty(axisItem, NeedSorting, out addResult);
        }
        /// <summary>
        /// Pro tuto kartu (sklad MRP a danou plánovací jednotku) založí nový záznam o změně množství
        /// </summary>
        /// <param name="axisItem">Záznam osy S o pohybu, pro něj (a s ním) se vytvoří záznam o změně stavu</param>
        /// <param name="isSuspendSorting">Příznak, že je pozastavený sorting = záznam se má bez přemýšlení přidat na konec soupisu</param>
        internal void AddChangeStockQty(MaterialPlanAxisItemCls axisItem, bool isSuspendSorting)
        {
            StockCardAddResult addResult;
            _AddChangeStockQty(axisItem, isSuspendSorting, out addResult);
        }
        /// <summary>
        /// Pro tuto kartu (sklad MRP a danou plánovací jednotku) založí nový záznam o změně množství
        /// </summary>
        /// <param name="axisItem">Záznam osy S o pohybu, pro něj (a s ním) se vytvoří záznam o změně stavu</param>
        /// <param name="isSuspendSorting">Příznak, že je pozastavený sorting = záznam se má bez přemýšlení přidat na konec soupisu</param>
        /// <param name="addResult">Out informace o důsledku přidání záznamu na osu - popisuje konflikt přidávaného záznamu o pohybu se stavem skladové karty.</param>
        internal void AddChangeStockQty(MaterialPlanAxisItemCls axisItem, bool isSuspendSorting, out StockCardAddResult addResult)
        {
            _AddChangeStockQty(axisItem, isSuspendSorting, out addResult);
        }
        /// <summary>
        /// Pro tuto kartu (sklad MRP a danou plánovací jednotku) založí nový záznam o změně množství
        /// </summary>
        /// <param name="axisItem">Záznam osy S o pohybu, pro něj (a s ním) se vytvoří záznam o změně stavu</param>
        /// <param name="isSuspendSorting">Příznak, že je pozastavený sorting = záznam se má bez přemýšlení přidat na konec soupisu</param>
        /// <param name="addResult">Out informace o důsledku přidání záznamu na osu - popisuje konflikt přidávaného záznamu o pohybu se stavem skladové karty.</param>
        private void _AddChangeStockQty(MaterialPlanAxisItemCls axisItem, bool isSuspendSorting, out StockCardAddResult addResult)
        {
            addResult = StockCardAddResult.NoAdded;

            // 1. Určím, jak při daném požadavku isSuspendSorting a aktuálním stavu NeedSorting budu pracovat se tříděním dat:
            bool noSorting = _IsDisabledSortingForInput(isSuspendSorting);

            // 2. Vytvoříme záznam o změně:
            StockChangeCls insertItem = new StockChangeCls(axisItem);

            // 3. Najdeme vhodný index pro Insert na linku:
            int insertIndex = _FindInsertIndex(insertItem, noSorting);

            // 4. Vložíme záznam do pole, zatím bez Recalc:
            int recalcIndex = _InsertChangeItem(insertIndex, insertItem);

            // 5. Určíme result:
            addResult = _GetAddResultState(insertItem.ChangeQty);

            // 6. Pokud jsme nyní data nesetřídili, zapamatujeme si že to bude zapotřebí před jejich čtením:
            if (noSorting)
                NeedSorting = true;
            else
                // Pokud jsou data tříděná, vyvoláme Recalc počínaje insertIndexem:
                _RecalcAxis(recalcIndex);

            // 7. Změna na kartě, příznak:
            this.IsChanged = true;
            this.StockCardState = StockCardStateType.Initialized;

            //// Brano 2012/12
            //if (this.PlanUnitSData.RecordNumber == 1122155 && (this.StockUnit.StockMrp == 985092 || this.StockUnit.StockMrp == 642782))
            //{   // Hlídaná PJ na jednom z hlídaných skladů (Brano 2012/12):
            //    Steward.AuditInfo("AddChangeStockQty for StockUnit: " + this.StockUnit.ToString());
            //    Steward.AuditInfo("   Add Item: " + axisItem.ToString());
            //}
        }
        /// <summary>
        /// Určí a vrátí výsledek přidání záznamu o změně množství (dle parametru) na skladovou kartu, za jejího aktuálního stavu.
        /// </summary>
        /// <param name="changeQty">Změna množství. Důležité je jen znaménko.</param>
        /// <returns></returns>
        private StockCardAddResult _GetAddResultState(decimal changeQty)
        {
            switch (this.StockCardState)
            {
                case StockCardStateType.Initialized:
                case StockCardStateType.CurrentlyCreatingProposals:
                    return StockCardAddResult.Correct;
                case StockCardStateType.ProposalsCreated:
                    return (changeQty >= 0M ? StockCardAddResult.AfterProposalCorrect : StockCardAddResult.AfterProposalError);
                case StockCardStateType.Balanced:
                case StockCardStateType.Saved:
                    return StockCardAddResult.AfterBalanceError;
            }
            return StockCardAddResult.AfterBalanceError;
        }
        /// <summary>
        /// Na této skladové kartě se pokusí najít daný záznam o změně, navázaný na osu S, a tento záznam o změně stavu skladu odebere z karty.
        /// Typicky se používá při přeplánování, kdy namísto jednoho záznamu může vzniknout jiný (v dalších krocích).
        /// </summary>
        /// <param name="axisItem">Data plánovací osy, která se odebírá z materiálové osy zásob</param>
        internal void RemoveChangeStockQty(MaterialPlanAxisItemCls axisItem)
        {
            if (axisItem == null)
                Throw.SysError(MessageInfo.Get("Ze skladové karty nelze odebrat záznam o pohybu, protože není specifikován (axisItem je null)."));
            this.RemoveChangeStockQty(axisItem.AxisID);
        }
        /// <summary>
        /// Na této skladové kartě se pokusí najít daný záznam o změně, navázaný na osu S, a tento záznam o změně stavu skladu odebere z karty.
        /// Typicky se používá při přeplánování, kdy namísto jednoho záznamu může vzniknout jiný (v dalších krocích).
        /// </summary>
        /// <param name="axisId">ID záznamu plánovací osy, která se odebírá z materiálové osy zásob</param>
        internal void RemoveChangeStockQty(int axisId)
        {
            int stockIndex = this._StockChanges.FindIndex(s => (s.AxisID == axisId));
            if (stockIndex >= 0)
            {
                this._StockChanges.RemoveAt(stockIndex);
                this.StockCardState = StockCardStateType.Initialized;
                this.IsChanged = true;
            }
        }
        /// <summary>
        /// Najde a vrátí položku osy S z daného indexu skladové karty (nejde tedy o AxisID !).
        /// Pokud je index mimo rozsah, vyhodí chybu.
        /// </summary>
        /// <param name="index">Index záznamu na skladové kartě</param>
        /// <returns>Data materiálové osy S</returns>
        internal MaterialPlanAxisItemCls GetPlanAxisSFromIndex(int index)
        {
            // Tady nemá být Sort, odkazujeme se na konrétní index:
            if (index < 0 || index >= this._StockChanges.Count)
                Throw.SysError(MessageInfo.Get("Je požadována položka skladové karty s indexem mimo povolený rozsah: %0 (povolený rozsah = 0 až %1).", index, this._StockChanges.Count));
            return this._StockChanges[index].MaterialPlanAxisItem;
        }
        /// <summary>
        /// Přesune záznam o změně skladové zásoby na jiný čas, zajistí přepočet linky
        /// </summary>
        /// <param name="axisId">Záznam osy - číslo záznamu</param>
        /// <param name="moveToTime">Datum a čas, na který chceme záznam přesunout</param>
        internal void MoveSAxisToTime(int axisId, DateTime moveToTime)
        {
            this.MoveSAxisToTime(axisId, moveToTime, NeedSorting);
        }
        /// <summary>
        /// Přesune záznam o změně skladové zásoby na jiný čas, zajistí přepočet linky.
        /// Nemění datum na navázaném záznamu osy S (this._MaterialPlanAxisItem).
        /// Důvod: v tomto místě není zřejmé, kdo je původcem změny, zda proces karty, nebo proces osy.
        /// Nechť si tedy původce zajistí změnu na ose S podle svého uvážení.
        /// Změna na ose může být prováděna zpětně (End = ToHistory = Begin) nebo naopak, a to zde nedokážeme rozhodovat.
        /// Navíc právě určení toho intervalu (Begin-End na ose S) je nad rámec zdejších možností.
        /// </summary>
        /// <param name="axisId">Záznam osy - číslo záznamu</param>
        /// <param name="moveToTime">Datum a čas, na který chceme záznam přesunout</param>
        /// <param name="isSuspendSorting">Příznak, že je pozastavený sorting = záznam se má přidat na konec soupisu</param>
        internal void MoveSAxisToTime(int axisId, DateTime moveToTime, bool isSuspendSorting)
        {
            // 1. Určím, jak při daném požadavku isSuspendSorting a aktuálním stavu NeedSorting budu pracovat se tříděním dat:
            bool noSorting = _IsDisabledSortingForInput(isSuspendSorting);

            // 2. Najdeme položku, která se bude měnit (její datum):
            int indexSource = _FindAxisRecordIndex(axisId);
            if (indexSource < 0)
            {
                Throw.SysError(MessageInfo.Get("Požadovaný záznam osy %0 nebyl na linii StockUnit=%1 nalezen.", axisId, this.StockUnit));
                return;
            }
            StockChangeCls changeItem = this._StockChanges[indexSource];          // Položka, která se bude měnit

            // 3. Pokud máme nyní data bez třídění, pak v položce jen změním datum a skončím:
            if (noSorting)
            {
                changeItem.SetChangeDate(moveToTime);                             // V proměnné změníme datum, tím se změní i v seznamu
                this.NeedSorting = true;       // IsChanged se nemění, ale nastaví se na true při příští rekalkulaci
                return;
            }

            // 4. Sort je živý => musíme přemístit položku na jiný vhodný index:
            this._StockChanges.RemoveAt(indexSource);                             // Položku máme v proměnné, vyřadíme ji ze seznamu
            changeItem.SetChangeDate(moveToTime);                                 // V proměnné změníme datum
            int indexInsert = _FindInsertIndex(changeItem, false);                // Pro upravenou proměnnou najdeme nový insert index
            int recalcIndex = _InsertChangeItem(indexInsert, changeItem);         // Vloží záznam z proměnné do pole, na vhodný index, bez rekalkulace návazných položek

            // 5. Vyvoláme Recalc počínaje vhodným indexem (ten menší: insert nebo source) :
            int indexRecalc = (indexSource < recalcIndex ? indexSource : recalcIndex);
            this._RecalcAxis(indexRecalc);             // Provede rekalkulaci, a nastaví this.IsChanged = false;
        }
        /// <summary>
        /// Vrátí zůstatek na ose na jejím úplném konci (tj. FlowQty poslední změny, anebo InitialStockQty pokud nejsou změny)
        /// </summary>
        /// <returns></returns>
        internal decimal GetResultStockQty()
        {
            int c = this.Items.Count;     // zajistí setřídění a přepočet pole _StockChanges, pokud je zapotřebí
            return (c == 0 ? this.InitialStockQty : this.Items[c - 1].FlowQty);
        }
        /// <summary>
        /// Určí disponibilní zásobu v daném čase
        /// </summary>
        /// <param name="requestedTime">Datum a čas, v němž chceme znát disponibilní zásobu</param>
        /// <returns>Disponibilní zásoba</returns>
        internal decimal GetDisponibleStockQty(DateTime requestedTime)
        {
            this.CheckChanges();          // zajistí setřídění a přepočet pole _StockChanges, pokud je zapotřebí
            int index = this._StockChanges.FindLastIndex(delegate(StockChangeCls scanItem)
            { return (scanItem.ChangeDate <= requestedTime); });
            if (index < 0) return this.InitialStockQty;
            return this._StockChanges[index].FlowQty;
        }
        /// <summary>
        /// Metoda zajistí setřídění a rekalkulaci vývoje zásoby, pokud je to zapotřebí.
        /// </summary>
        internal void CheckChanges()
        {
            if (this.NeedSorting)
                this.SortAndRecalc();                  // Sort a rekalkulace
            else if (this.IsChanged)
                this.SortAndRecalc();                  // Sort a rekalkulace
        }
        /// <summary>
        /// Pokud tato karta není setříděná (má hodnotu NeedSorting = true), 
        /// pak setřídí změnové záznamy na této lince a přepočte jejich koncových zásob.
        /// Pokud setříděná již je, nedělá nic.
        /// </summary>
        internal void SortIfNeed()
        {
            if (this.NeedSorting)
                this.SortAndRecalc();
        }
        /// <summary>
        /// Setřídí záznamy SAxis na této lince a přepočet jejich koncových zásob
        /// </summary>
        internal void SortAndRecalc()
        {
            this._SortAxis();
            this._RecalcAxis();
        }
        /// <summary>
        /// Nastaví stav skladové karty na danou hodnotu.
        /// Kontroluje přípustnost nového stavu s ohledem na aktuální stav.
        /// </summary>
        /// <param name="stockCardState"></param>
        internal void SetStockCardState(StockCardStateType stockCardState)
        {
            int oldState = (int)this.StockCardState;             // Enum má tu vlastnost, že stav lze pouze zvyšovat.
            int newState = (int)stockCardState;
            if (newState == oldState) return;
            if (newState < oldState)
                Throw.SysError(MessageInfo.Get("Chyba: pro skladovou kartu nelze nastavit její stav na %0, protože se nachází ve stavu %1. Stav je možno nastavovat pouze \"nahoru\".", stockCardState, this.StockCardState));
            this.StockCardState = stockCardState;
        }
        /// <summary>
        /// Metoda najde a vrátí záznam o pohybu (změna stavu, odpovídá AxisS) pro daný klíč AxisID.
        /// Pokud nenajde, vrátí null.
        /// </summary>
        /// <param name="axisId"></param>
        /// <returns></returns>
        internal StockChangeCls FindStockChange(int axisId)
        {
            return this._StockChanges.Find(s => (s.AxisID == axisId));
        }
        /// <summary>
        /// Metoda najde a vrátí číslo indexu záznamu o pohybu (změna stavu, odpovídá AxisS) pro daný klíč AxisID.
        /// Pokud nenajde, vrátí -1.
        /// </summary>
        /// <param name="axisId"></param>
        /// <returns></returns>
        internal int FindStockChangeIndex(int axisId)
        {
            return this._StockChanges.FindIndex(s => (s.AxisID == axisId));
        }
        /// <summary>
        /// Převede string z editačního stylu [kkzm_kumul_met] {"C"=Časová; "M"=Množstevní; "N"=Žádná} na odpovídající hodnotu enumu
        /// </summary>
        /// <param name="kumulaceNavrhy">{"C"=Časová; "M"=Množstevní; "N"=Žádná}</param>
        /// <returns></returns>
        internal static StockCardCumulationMode ConvertStringToStockCardCumulationMode(string kumulaceNavrhy)
        {
            if (kumulaceNavrhy == null) return StockCardCumulationMode.None;
            switch (kumulaceNavrhy)
            {
                case "C":
                    return StockCardCumulationMode.Time;
                case "M":
                    return StockCardCumulationMode.Quantity;
            }
            return StockCardCumulationMode.None;
        }
        #endregion
        #region REKALKULACE PRŮBĚŽNÉ ZÁSOBY A URČENÍ VÝSLEDNÉ ZÁSOBY PODLE KRITÉRIÍ, DO ZÁZNAMŮ OSY A DO STAVU SKLADU PLÁNOVACÍ JEDNOTKY
        /// <summary>
        /// Přepočítá vývoj disponibilní zásoby včetně rozšířených výsledků, typicky pro ukládání dat do databáze.
        /// Pouze pokud od posledního přepočtu jsou změny.
        /// </summary>
        internal void RecalcDisponibleQtyIfChanged()
        {
            if (this.IsChanged)
                this._RecalcDisponibleQty(true);
        }
        /// <summary>
        /// Přepočítá vývoj disponibilní zásoby včetně rozšířených výsledků, typicky pro ukládání dat do databáze.
        /// Vždy, i když nejsou změny.
        /// </summary>
        internal void RecalcDisponibleQty(bool force)
        {
            this._RecalcDisponibleQty(force);
        }
        /// <summary>
        /// Přepočítá vývoj disponibilní zásoby včetně rozšířených výsledků, typicky pro ukládání dat do databáze.
        /// </summary>
        private void _RecalcDisponibleQty(bool force)
        {
            // Pokud není změna a současně není požadavek na "force" (tj. povinný přepočet), tak skončím:
            if (!this.IsChanged && !force) return;

            this.SortIfNeed();

            // Průběžné množství, na základě výchozího množství k dispozici:
            StockChangeFlowSummaryCls flowQty = new StockChangeFlowSummaryCls(this.InitialStockQty);
            for (int i = 0; i < this._StockChanges.Count; i++)
            {
                // Vývoj zásoby :
                StockChangeCls stockChange = this._StockChanges[i];

                // Aktualizovat průběžný stav vývoje zásoby:
                flowQty.GetFromAxis(stockChange.MaterialPlanAxisItem);

                // Zapsat průběžný stav na osu, do skladových kolonek:
                flowQty.SetToAxisSStock(stockChange.MaterialPlanAxisItem);
            }

            // Výsledná zásoba do stavu skladu:
            this.SlQtyFinalDisp = flowQty.FlowQtyProp;
            this.SlQtyFinal = flowQty.FlowQtyProp + this.SlQty - this.SlQtyDisp;
            this.SlDateFirstProp = flowQty.DateFirstProposal;

            this.IsChanged = false;
        }
        #endregion
        #region PRIVATE METODY
        /// <summary>
        /// Najde místo na lince, kam je třeba vložit daný záznam, aby byla korektní časová osa linky.
        /// </summary>
        /// <param name="insertItem">Záznam pro vložení</param>
        /// <param name="noSorting">Příznak, že se data nemají třídit = záznam se má přidat na konec soupisu</param>
        /// <returns>Index pro Insert, nebo -1 = přidat na konec.</returns>
        private int _FindInsertIndex(StockChangeCls insertItem, bool noSorting)
        {
            if (noSorting)
                return -1;

            // Najdeme index na ose, kam nový záznam přidáme (podle data a typu záznamu) 
            //  (tj. najdeme index existujícího prvku, který bude první odsunutý ke konci):
            //  Technicky: použijeme stadnardní komparátor StockChangeCls.CompareByAxisOrder (který nám třídí osu, která je vždy korektně setříděná !)
            //    a vyhledáme první index (FindIndex) záznamu (StockChangeCls scanItem), pro který komparátor vyhodnotí,
            //    že vkládaný záznam je menší (CompareByAxisOrder(insertItem, scanItem) < 0) 
            //    => našli jsme záznam, který je první větší než nově vkládaný, a máme jeho index.
            int insertIndex = this._StockChanges.FindIndex(delegate(StockChangeCls scanItem)
            { return (StockChangeCls.CompareByAxisOrder(insertItem, scanItem) < 0); });
            // Považuji tento postup za rychlejší, než přidat na konec, pak Sort a pak kompletní Recalc.
            return insertIndex;
        }
        /// <summary>
        /// Najde index, na němž se nyní nachází záznam osy s daným číslem.
        /// Zatím nevedu indexové pole (record = index), kvůli jeho údržbě. Možná začnu ?
        /// </summary>
        /// <param name="axisId">Záznam osy - číslo záznamu</param>
        /// <returns>Index pole </returns>
        private int _FindAxisRecordIndex(int axisId)
        {
            return (this._StockChanges.FindIndex(delegate(StockChangeCls scanItem)
            { return (scanItem.MaterialPlanAxisItem.AxisID == axisId); }));
        }
        /// <summary>
        /// Vloží do své linky nový záznam
        /// </summary>
        /// <param name="insertIndex">Index ke vložení</param>
        /// <param name="insertItem">Záznam k vložení</param>
        /// <returns>Index prvku, od něhož se má rekalkulovat linka pro přepočet FlowQty</returns>
        private int _InsertChangeItem(int insertIndex, StockChangeCls insertItem)
        {
            // Vložíme nový záznam na daný index:
            int recalcIndex = 0;
            if (insertIndex >= 0)
            {	// Pokud jsme našli nějaký záznam, na jehož místo insertujeme nový:
                this._StockChanges.Insert(insertIndex, insertItem);
                recalcIndex = insertIndex;
            }
            else
            {	// Nenašli jsme => záznam přidáme na konec:
                this._StockChanges.Add(insertItem);
                recalcIndex = this._StockChanges.Count - 1;
            }
            this.IsChanged = true;
            this.StockCardState = StockCardStateType.Initialized;
            return recalcIndex;
        }
        /// <summary>
        /// Detekuje potřebu udržovat data setřídění pro potřeby VSTUPU dat do tohoto objektu (vkládání, mazání, změna data).
        /// Na vstupu je požadavek na Suspend sorting : true = vypnout třídění při zadávání dat.
        /// Vrací true, pokud je na vstupu true, anebo je v tuto chvíli již nastaveno this.NeedSorting = true, tj. máme nesetříděná data.
        /// </summary>
        /// <param name="isSuspendSorting"></param>
        /// <returns></returns>
        private bool _IsDisabledSortingForInput(bool isSuspendSorting)
        {
            return (isSuspendSorting || NeedSorting);
        }
        /// <summary>
        /// Setřídí záznamy na ose.
        /// </summary>
        private void _SortAxis()
        {
            this._StockChanges.Sort(StockChangeCls.CompareByAxisOrder);
            this.NeedSorting = false;
        }
        /// <summary>
        /// Přepočte osu počínaje od záznamu na daném indexu, plus všechny další
        /// </summary>
        private void _RecalcAxis()
        {
            _RecalcAxis(0, true);
        }
        /// <summary>
        /// Přepočte osu počínaje od záznamu na daném indexu, plus všechny další
        /// </summary>
        /// <param name="recalcIndex">Počáteční index přepočtu. Bez zadání indexu přepočte vše, až do konce.</param>
        private void _RecalcAxis(int recalcIndex)
        {
            _RecalcAxis(recalcIndex, false);
        }
        /// <summary>
        /// Přepočte osu počínaje od záznamu na daném indexu, plus všechny další
        /// </summary>
        /// <param name="recalcIndex">Počáteční index přepočtu. Bez zadání druhého parametru forceToEnd se chová jako by forceToEnd bylo false.</param>
        /// <param name="forceToEnd">Povinně až do konce? true = ano, false = pokud někde narazí na stav, kde je result bez potřeby změny, dál už nejde.</param>
        private void _RecalcAxis(int recalcIndex, bool forceToEnd)
        {
            if (recalcIndex < 0) recalcIndex = 0;
            // Počítáme průběžnou zásobu = FlowQty (ta zohledňuje položky na kartě, které jsou / nejsou Hidden)
            decimal qtyFlow = ((recalcIndex == 0) ? this.InitialStockQty : this._StockChanges[recalcIndex - 1].FlowQty);
            int lastIndex = this._StockChanges.Count - 1;
            for (int i = recalcIndex; i <= lastIndex; i++)
            {
                StockChangeCls change = this._StockChanges[i];     // záznam o změně stavu
                decimal qtyOld = change.FlowQty;                   // nynější hodnota, před přepočtem
                qtyFlow = change.RecalcFlowQty(qtyFlow);           // provedení přepočtu (.FlowQty = qtyFlow + .ChangeQty)
                if (forceToEnd || change.FlowQty != qtyOld)        // Pokud je povinností kalkulovat vše, anebo pokud se liší původní FlowQty od nynější hodnoty FlowQty
                    continue;                                      //  -> pak pokračuji dál.
                if (i < lastIndex && qtyFlow != this._StockChanges[i + 1].PrevQty)  // Pokud zpracováváme položku, která není poslední, a průběžné množství na konci této položky je jiné než počáteční množství na přísští položce:
                    continue;                                      //  -> pak pokračuji dál. Jinak skončíme:
                break;
            }
            this.IsChanged = false;
        }
        #endregion
        #region PODPORA PRO PLÁNOVÁNÍ: TVORBA SEZNAMU PRIMÁRNÍCH (NEUPRAVOVANÝCH) NÁVRHŮ NA PŘÍJEM
        /// <summary>
        /// Metoda vygeneruje návrhy na příjem pro tuto skladovou kartu.
        /// Vytvořené návrhy jsou jednotkové = jde o skutečně potřebnou zásobu v daném čase, bez kumulací.
        /// Vytvořené návrhy NEZOHLEDŇUJÍ koeficienty na stavu skladu (optimum, dělitel, maximum).
        /// Pokud při tvorbě návrhů na příjem podle zde vytvořených podkladů bude navrhované množství navýšeno (snížit jej nelze),
        /// pak je nanejvýš vhodné o toto navýšení na oplátku snížit (anebo docela zrušit) příští návrh,
        /// tak aby zarovnání množství "nahoru" se příště zase spotřebovalo.
        /// Návrhy jsou oddělené i podle jejich původu (návrhy pro zdejší minimální zásobu jsou oddělené od návrhů do poptávek a dalších výdejů).
        /// V průběhu tvorby návrhů dochází k přesouvání záznamů o příjmech (jejich předatování směrem ToHistory), 
        /// pokud jejich množství je potřebné pro zajištění požadavků v daném datu.
        /// Informace o přesunu příjmu v čase je zjistitelná na záznamu osy.
        /// </summary>
        /// <param name="planningParameters"></param>
        /// <returns></returns>
        internal List<PlanningStockCardProposalCls> CreatePrimaryProposals(PlanningParametersCls planningParameters, ref List<PlanningItemRelationCls> relationList)
        {
            #region Iniciace lokálních proměnných
            this.Audit("CreatePrimaryProposals.Before");
            List<PlanningStockCardProposalCls> result = new List<PlanningStockCardProposalCls>();
            this._CreateProposalsPrepareParams(planningParameters);   // Připraví cachované parametry (this._StockParams)
            this._CreateProposalInit(planningParameters);             // Připraví data v instanci skladové karty pro tvorbu návrhů, připraví záznamy osy S do výchozího stavu
            #endregion
            #region Zpracování příjmů (do balanceru a do požadavků na Scheduling) a výdejů (do pole issueList); nachystání výrobních příkazů do schedulingu
            List<StockChangeCls> stockChanges = this.Items;
            List<StockChangeCls> issueList = new List<StockChangeCls>();   // Změny na stavu skladu = Výdeje
            AxisQtyBallancer ballancer = new AxisQtyBallancer();           // Balancer - řeší správu disponibilní zásoby a tvorbu vztahů o průběžném balancování mezi existujícím zajištěním a požadavky
            if (this.InitialStockQty > 0M)
                ballancer.AddSource(this.InitialStockQty, PlanSAxisOriginReason.NoReason);
            for (int i = 0; i < stockChanges.Count; i++)
            {
                StockChangeCls stockChange = stockChanges[i];
                MaterialPlanAxisItemCls axisItem = stockChange.MaterialPlanAxisItem;
                if (stockChange.PlanningState == PlanSAxisPlanningState.Active)
                {
                    if (stockChange.ChangeQty >= 0M)
                        // Příjem:
                        ballancer.AddSource(axisItem);
                    else
                        // Výdej:
                        issueList.Add(stockChange);

                    if (axisItem.IsReceiptFinalProduct && !axisItem.IsPreparedOrScheduled)
                        // Výrobní příkaz:
                        result.Add(PlanningStockCardProposalCls.CreateScheduling(axisItem));
                }
            }
            #endregion
            #region Minimální zásoba: datový střadač, výchozí zásoba (minimální nebo nezáporné množství)
            StockCardMinQtyData minData = this._GetMinQtyData(planningParameters);
            decimal initialMinQty = minData.GetInitialQty();
            if (initialMinQty > 0M && this.IsPlanning)
            {   // Pokud je zapotřebí navýšit výchozí zásobu kvůli minimální zásobě (a karta se plánuje), pak se pokusím to řešit:
                // Skrze balancer odeberu z existujících zajištění to, co lze (vytvoří se vztahy AxisBalance):
                decimal remainingMinQty = ballancer.DeQueueQty(initialMinQty, GID.Empty, minData.InitialDate, PlanSAxisOriginReason.MinimumStock, PlanningItemRelationType.AxisBalance, ref relationList);
                minData.AddInitialReserving(initialMinQty - remainingMinQty); // Kolik jsem spotřeboval pro výchozí zásobu
                minData.AddInitialProposal(remainingMinQty);                  // Kolik ještě potřebuji navrhnout na příjem
            }
            #endregion
            #region Průběžný vývoj zásoby podle požadavků, odebíráme z balanceru a vytváříme návrhy na zajištění pokud zásoby dojdou
            if (this.IsPlanning)
            {
                foreach (StockChangeCls issue in issueList)
                {
                    MaterialPlanAxisItemCls axisItem = issue.MaterialPlanAxisItem;
                    decimal disponibleQtyBefore = ballancer.RemainingQty;
                    decimal missingQty = ballancer.DeQueueQty(axisItem, PlanningItemRelationType.AxisBalance, ref relationList);       // Najdu zdroje mezi existujícími příjmy a daným požadavkem, odeberu z nich, vrátím množství nepokryté
                    bool createProposal = (missingQty > 0m);
                    decimal disponibleQtyAfter = ballancer.RemainingQty;
                    minData.AddFlowRemainingInfo(axisItem.DatePlanEnd, disponibleQtyAfter, createProposal);

                    // Pro záznam osy S jsme nenašli dostatečné zajištění => vytvoříme návrh na příjem:
                    if (createProposal)
                        result.Add(PlanningStockCardProposalCls.CreateProposalRequest(axisItem, missingQty, disponibleQtyBefore, this._StockParams.MinStockQty));
                }
            }
            #endregion
            #region Detekce minimální zásoby na konci balancování, vytvoření návrhu na zajištění minimální (nebo nezáporné) zásoby
            minData.AddEndRemainingInfo(ballancer.RemainingQty);
            if (minData.ContainProposal)
                result.Add(minData.CreateProposalMinQty());
            #endregion
            #region KONEC
            // Pokud v balanceru něco zbylo, vybalancuji to do nikam.
            // Vztahy typu Output se zde nevytvářejí, ty se tvoří centrálně v metodě AxisHeapCls.MapFinalRelation()
            ballancer.DeQueueRemainder(PlanningItemRelationType.AxisBalance, ref relationList);
            ((IDisposable)ballancer).Dispose();
            this._StockParams = null;
            this.AxisBalanceExists = true;                // Nastavím příznak na true, i když vztahy do systému AxisHeap nepřidávám (ale nejspíš se tam přidají).
            this.Audit("CreatePrimaryProposals.After");
            return result;
            #endregion
        }
        /// <summary>
        /// Vrátí objekt pro řízení minimální zásoby
        /// </summary>
        /// <param name="planningParameters"></param>
        /// <returns></returns>
        private StockCardMinQtyData _GetMinQtyData(PlanningParametersCls planningParameters)
        {
            StockCardMinQtyData minData = new StockCardMinQtyData(this);
            minData.MinQtyMode = this._StockParams.MinStockQtyMode;
            minData.InitialStockQty = this.InitialStockQty;
            minData.MinStockQty = this._StockParams.MinStockQty;
            minData.InitialDate = planningParameters.InitialDateMinStockProposal;
            minData.FirstIssueBelowMinQty = null;
            minData.NeedQty = null;
            return minData;
        }
        /// <summary>
        /// Připraví data v instanci skladové karty pro tvorbu návrhů.
        /// Zruší všechny návrhy na změnu data příjmu na sklad (pokud byly), a v případě potřeby znovu setřídí skladovou osu.
        /// Nastaví index this._FirstReceiptForTransfer (ukazatel na příští možný příjem pro přesun v čase) na hodnotou -1.
        /// </summary>
        private void _CreateProposalInit(PlanningParametersCls planningParameters)
        {
            // 1. Zruší všechny přesuny v čase u položek, které to umožňují, podmínečně provede přetřídění:
            bool needSort = false;
            for (int i = 0; i < this._StockChanges.Count; i++)
            {
                StockChangeCls stockChange = this._StockChanges[i];

                // Vynuluji spotřebované množství:
                if (stockChange.MaterialPlanAxisItem.QtyConsumed != 0M)
                    stockChange.MaterialPlanAxisItem.QtyConsumed = 0M;

                // Do hledání zahrneme jen aktivní záznamy (pokud by byl záznam neaktivní nebo skrytý, nebude se plánovat):
                if (stockChange.PlanningState != PlanSAxisPlanningState.Active) continue;

                MaterialPlanAxisItemCls axisItem = stockChange.MaterialPlanAxisItem;
                stockChange.MaterialPlanAxisItem.MoveDateReset();
                if (stockChange.ChangeDate != stockChange.MaterialPlanAxisItem.DatePlanEnd)
                {	// Pokud došlo ke změně data na ose S, upravím si datum na záznamu skladové karty a zajistím resort a recalc:
                    stockChange.SetChangeDate(stockChange.MaterialPlanAxisItem.DatePlanEnd);
                    if (!needSort)
                        needSort = true;
                }
            }
            if (needSort)
                this.SortAndRecalc();

            // 2. Proběhne inicializace ukazatele na nejbližší dosud nepřesunutý příjem (viz metoda this._CreateProposalTransferReceipts()):
            this._FirstReceiptForTransfer = -1;
        }
        /// <summary>
        /// Obsahuje true, pokud tato skladová karta má nějaké pohyby anebo její výchozí zásoba je pod minimální zásobou.
        /// V takovém případě vrací true a je třeba zavolat metodu CreateProposals() pro přípravu návrhů na příjem.
        /// </summary>
        internal bool CreateProposalsIsUseful { get { return (this._StockChanges.Count > 0 || this.InitialStockQty < this.MinStockQty); } }
        #endregion
        #region class StockCardMinQtyData : controler pro řízení minimální zásoby
        /// <summary>
        /// Data popisující minimální zásobu
        /// </summary>
        protected class StockCardMinQtyData
        {
            internal StockCardMinQtyData(StockCardCls owner)
            {
                this.Owner = owner;
            }
            internal StockCardCls Owner { get; private set; }
            /// <summary>
            /// Režim udržování minimální zásoby
            /// </summary>
            internal MinimumStockQtyMode MinQtyMode { get; set; }
            /// <summary> { get; }
            /// Výchozí disponibilní zásoba na stavu skladu (sumárně ze všech skladů podřízených jednomu MRP skladu).
            /// Je napočtena ze stavů skladů, ale jen pokud je povoleno v parametrech MRP (MrpAcceptStockFund == true).
            /// Od této hodnoty se vychází při zajišťování materiálu v MPR.
            /// Liší se od hodnoty this.SlQtyDisp právě tou podmínkou (MrpAcceptStockFund == true). 
            /// this.SlQtyDisp obsahuje vždy disponibilní výchozí zásobu, kdežto this.InitialStockQty ji obsahuje jen když je povoleno s ní pracovat.
            /// </summary>
            internal decimal InitialStockQty { get; set; }
            /// <summary>
            /// Povinná minimální zásoba dle stavu skladu
            /// </summary>
            internal decimal MinStockQty { get; set; }
            /// <summary>
            /// Datum procesu + rezerva, nebo datum prvního výdeje pokud existuje.
            /// K tomuto datu se zajistí minimální zásoba, pokud výchozí zásoba je pod nulou nebo pod minimem.
            /// </summary>
            internal DateTime InitialDate { get; set; }
            /// <summary>
            /// Datum, kdy byl proveden první výdej, který vydal pod minimální zásobu.
            /// Nemusí dojít k vytvoření návrhu na zajištění pro výdej (pokud výsledná zásoba je menší než MinQty, ale větčí než 0).
            /// Ale k tomuto datu můžeme vytvořit návrh na zajištění minimální zásoby, podle nastavení.
            /// </summary>
            internal DateTime? FirstIssueBelowMinQty { get; set; }
            /// <summary>
            /// Datum, kdy byl vytvořen první návrh na zajištění pro běžný požadavek (ne minimální zásoba).
            /// Podle nastavení se může návrh na zajištění minimální zásoby vytvořit k tomuto datu.
            /// </summary>
            internal DateTime? FirstProposalBelowMinQty { get; set; }
            /// <summary>
            /// Potřebné množství do návrhu na zajištění MinZas
            /// </summary>
            internal decimal? NeedQty { get; set; }
            /// <summary>
            /// Množství odebrané z reálných zajištění za pokrytí výchozí minimální zásoby
            /// </summary>
            internal decimal InitialIssueQty { get; private set; }
            /// <summary>
            /// Množství do návrhu na zajištění minimální zásoby na počátku osy
            /// </summary>
            internal decimal InitialProposalQty { get; private set; }
            /// <summary>
            /// Množství do návrhu na zajištění minimální zásoby v průběhu osy
            /// </summary>
            internal decimal FlowProposalQty { get; private set; }
            /// <summary>
            /// Množství do návrhu na zajištění minimální zásoby na konci balancování
            /// </summary>
            internal decimal EndProposalQty { get; private set; }
            /// <summary>
            /// Množství do návrhu na zajištění minimální zásoby (celkem: InitialProposalQty + FlowProposalQty + EndProposalQty)
            /// </summary>
            internal decimal CurrentProposalQty { get { return this.InitialProposalQty + this.FlowProposalQty + this.EndProposalQty; } }
            /// <summary>
            /// Příznak, zda objekt obsahuje data pro vytvoření návrhu na MinQty (metodou CreateProposalMinQty())
            /// </summary>
            internal bool ContainProposal
            {
                get
                {
                    return this.CurrentProposalQty > 0m &&
                        (this.MinQtyMode == MinimumStockQtyMode.OnFirstProposal || this.MinQtyMode == MinimumStockQtyMode.OnPlanBegin);
                }
            }
            /// <summary>
            /// Vrátí množství, které je třeba pro zajištění daného stavu zásoby při aktuálním stavu zásob
            /// </summary>
            /// <returns></returns>
            internal decimal GetInitialQty()
            {
                decimal minQty = 0m;
                if (this.MinQtyMode == MinimumStockQtyMode.OnPlanBegin && this.MinStockQty > 0m)
                    // Pokud mám zajistit minimální zásobu hned na počátku procesu (a je uvedená), pak ji převezmu:
                    minQty = this.MinStockQty;
                // Pokud by výchozí disponibilní zásoba byla záporná (což je teoreticky možné), pak potřebné výchozí množství navýším o doplnění tohoto záporu:
                if (this.InitialStockQty < 0m)
                    minQty -= this.InitialStockQty;
                return (minQty < 0m ? 0m : minQty);
            }
            /// <summary>
            /// Zapamatuje si, kolik z reálných příjmů jsem spotřeboval pro zajištění výchozí minimální zásoby
            /// </summary>
            /// <param name="initialIssueQty"></param>
            internal void AddInitialReserving(decimal initialIssueQty)
            {
                if (initialIssueQty > 0m)
                    this.InitialIssueQty += initialIssueQty;
            }
            /// <summary>
            /// Zapamatuje si, na jaké množství je nutno připravit návrh na zajištění výchozí minimální zásoby
            /// </summary>
            /// <param name="initialProposalQty"></param>
            internal void AddInitialProposal(decimal initialProposalQty)
            {
                if (initialProposalQty > 0m)
                    this.InitialProposalQty += initialProposalQty;
            }
            /// <summary>
            /// Zapamatuje si, kdy poprvé došlo k poklesu zásoby pod minimální hladinu / pod nulu.
            /// Zapamatuje si první datum podtečení minimální zásoby, a první datum kdy je vytvářen návrh na zajištění pro běžný výdej.
            /// </summary>
            /// <param name="dateIssue">Datum výdeje</param>
            /// <param name="disponibleQtyAfter">Disponibilní zásoba po odebrání z existujících zajištění, Nikdy není pod nulou (požadavek v první fázi čerpá jen disponibilní zásobu).</param>
            /// <param name="createProposal">Příznak, že se bude vytvářet návrh (požadavek přesahuje disponibilní zásobu, musí se navrhnout další zajištění)</param>
            internal void AddFlowRemainingInfo(DateTime dateIssue, decimal disponibleQtyAfter, bool createProposal)
            {
                if (this.MinStockQty > 0m)
                {   // Pokud hlídáme minimální zásobu:
                    this.Audit("AddFlowRemainingInfo Date=" + dateIssue.ToString());

                    decimal dispWithProp = (disponibleQtyAfter < 0m ? 0m : disponibleQtyAfter) + this.CurrentProposalQty;   // Disponibilní zásoba po napočtení současného návrhu na zajištění MinQty (CurrentProposalQty = úvodní + průběžný + koncový)
                    decimal addFlowProposal = this.MinStockQty - this.InitialIssueQty - dispWithProp;                       // Množství k přidání do průběžného návrhu = kolik chybí do minimální zásoby
                    
                    // Pokud budeme navyšovat návrh na příjem MinQty, pak přidáme addFlowProposal:
                    if (addFlowProposal > 0m)
                        this.FlowProposalQty += addFlowProposal;

                    // Pokud máme nastřádán návrh na zajištění MinQty z titulu průběžné zásoby, budeme registrovat první datum:
                    if (this.CurrentProposalQty > 0m)
                    {
                        if (!this.FirstIssueBelowMinQty.HasValue)
                            this.FirstIssueBelowMinQty = dateIssue;
                        if (!this.FirstProposalBelowMinQty.HasValue && createProposal)
                            this.FirstProposalBelowMinQty = dateIssue;
                    }
                }
            }
            /// <summary>
            /// Zapamatuje si závěrečné množství, a podle něj a podle konfigurace a průběžného vývoje zásoby určí, zda bude třeba vytvořit návrh na zajištění minimální zásoby.
            /// </summary>
            /// <param name="disponibleQtyEnd"></param>
            internal void AddEndRemainingInfo(decimal disponibleQtyEnd)
            {
                if (this.MinStockQty > 0m)
                {   // Pokud hlídáme minimální zásobu:
                    this.Audit("AddEndRemainingInfo Qty=" + disponibleQtyEnd.ToString());

                    decimal dispWithProp = (disponibleQtyEnd < 0m ? 0m : disponibleQtyEnd) + this.CurrentProposalQty;       // Disponibilní zásoba po napočtení současného návrhu na zajištění MinQty (CurrentProposalQty = úvodní + průběžný + koncový)
                    decimal addEndProposal = this.MinStockQty - dispWithProp - this.InitialIssueQty;                        // Množství k přidání do průběžného návrhu = kolik chybí do minimální zásoby

                    // Pokud budeme navyšovat návrh na příjem MinQty, pak přidáme addEndProposal:
                    if (addEndProposal > 0m)
                        this.EndProposalQty += addEndProposal;
                }
            }
            /// <summary>
            /// Vytvoří a do patřičných dat uloží návrh na zajištění minimální zásoby.
            /// Uvnitř objektu jsou uloženy podklady pro určení data, množství a směru času.
            /// </summary>
            internal PlanningStockCardProposalCls CreateProposalMinQty()
            {
                if (!this.ContainProposal) return null;

                this.Audit("CreateProposalMinQty");

                DateTime dateProposal = this.InitialDate;
                TimeRange.TimeDirection timeDir = TimeRange.TimeDirection.ToFuture;
                if (this.MinQtyMode == MinimumStockQtyMode.OnFirstProposal && this.FirstProposalBelowMinQty.HasValue)
                {   // Pokud je nastaven režim: zajistit minimální zásobu až v okamžiku prvního návrhu na příjem, a máme datum takového návrhu (v FirstProposalBelowMinQty),
                    //  pak návrh MinQty datujeme podle něj:
                    dateProposal = this.FirstProposalBelowMinQty.Value;
                    timeDir = TimeRange.TimeDirection.ToHistory;
                }
                if (!this.FirstProposalBelowMinQty.HasValue && this.FirstIssueBelowMinQty.HasValue)
                {   // Pokud nemáme datum prvního návrhu, ale máme datum prvního podtečení MinQty (FirstIssueBelowMinQty), 
                    //  pak návrh MinQty datujeme podle tohoto podtečení:
                    dateProposal = this.FirstIssueBelowMinQty.Value;
                    timeDir = TimeRange.TimeDirection.ToHistory;
                }   // Jinak bude návrh MinQty datován počátkem procesu ToFuture.

                return PlanningStockCardProposalCls.CreateProposalMinQty(dateProposal, timeDir, this.CurrentProposalQty, 0M, this.MinStockQty);
            }
            /// <summary>
            /// Zápis info do auditlogu
            /// </summary>
            /// <param name="info"></param>
            internal void Audit(string info)
            {
                if (!Steward.AppConfig.IsDebug(this.Owner.StockUnit.PlanUnitS, this.Owner.StockUnit.StockMrp)) return;

                string dateFmt = "dd.MM.yyyy HH:mm";
                string line = "AuditMinQty " + info +
                    "; PlanUnitS=" + this.Owner.StockUnit.PlanUnitS.ToString() +
                    "; Stock=" + this.Owner.StockUnit.StockMrp.ToString() +
                    "; MinStockQty=" + this.MinStockQty.ToString() +
                    "; InitialStockQty=" + this.InitialStockQty.ToString() +
                    "; InitialDate=" + this.InitialDate.ToString(dateFmt) +
                    "; MinQtyMode=" + this.MinQtyMode.ToString() +
                    "; FirstIssueBelowMinQty=" + (this.FirstIssueBelowMinQty.HasValue ? this.FirstIssueBelowMinQty.Value.ToString(dateFmt) : "null") +
                    "; FirstProposalBelowMinQty=" + (this.FirstProposalBelowMinQty.HasValue ? this.FirstProposalBelowMinQty.Value.ToString(dateFmt) : "null");
                Steward.Audit(AuditRowType.Info, line);
            }
        }
        #endregion
        #region class AxisQtyBallancer : balancer množství mezi příjmy a výdaji
        /// <summary>
        /// AxisQtyBallancer : balancer množství mezi příjmy a výdaji
        /// </summary>
        protected class AxisQtyBallancer : IDisposable
        {
            internal AxisQtyBallancer()
            {
                this._BallancerList = new List<BalItem>();
            }
            void IDisposable.Dispose()
            {
                this._BallancerList = null;
            }
            public override string ToString()
            {
                return "RemainingQty=" + this.RemainingQty.ToString() + "; ItemCount=" + this._BallancerList.Count.ToString();
            }
            /// <summary>
            /// Příznak, že v balanceru není ani "ň"
            /// </summary>
            internal bool Empty { get { return (this.RemainingQty <= 0M); } }
            /// <summary>
            /// Aktuální zbývající množství v balanceru
            /// </summary>
            internal decimal RemainingQty { get { return this._BallancerList.Sum(b => b.RemainingQty); } }
            /// <summary>
            /// Do balanceru přidá další zdroj disponibilní zásoby.
            /// Tato varianta se používá pro zadání přírůstku zásoby daného příjmem na osu S.
            /// </summary>
            /// <param name="sourceAxisItem">Záznam osy o příjmu</param>
            internal void AddSource(MaterialPlanAxisItemCls sourceAxisItem)
            {
                this._BallancerList.Add(new BalItem(sourceAxisItem));
            }
            /// <summary>
            /// Do balanceru přidá další zdroj disponibilní zásoby.
            /// Tato varianta se používá pro zadání výchozí zásoby ze stavu skladu, bez zadání GIDu záznamu osy S.
            /// </summary>
            /// <param name="sourceQty">Množství</param>
            /// <param name="originReason">Důvod</param>
            internal void AddSource(decimal sourceQty, PlanSAxisOriginReason originReason)
            {
                this._BallancerList.Add(new BalItem(sourceQty, originReason));
            }
            /// <summary>
            /// Z balanceru odebere tolik zdrojů, aby pokryl požadované množství, a vygeneruje přiměřené vztahy.
            /// Vztahy přidává do ref parametru relationList.
            /// Pokud je na vstupu nula nebo menší množství, negeneruje žádný vztah.
            /// Pokud je na vstupu větší množství než je k dispozici, pak na výstupu metody bude nepokryté (chybějící) množství.
            /// Pokud je vše pokryto, nebo požadavek byl menší než 0, vrací 0M.
            /// Odebrané množství vepisuje do položek balanceru (=záznamy osy S) do property QtyConsumed.
            /// </summary>
            /// <param name="targetAxisItem"></param>
            /// <param name="relationType">Druh vztahu, vkládá se do výstupních záznamů</param>
            /// <param name="relationList">Soupis vztahů, kam se nové vztahy přidávají. Pokud je na vstupu null, vytvoří se zde empty.</param>
            /// <returns></returns>
            internal decimal DeQueueQty(MaterialPlanAxisItemCls targetAxisItem, PlanningItemRelationType relationType, ref List<PlanningItemRelationCls> relationList)
            {
                return this.DeQueueQty(targetAxisItem.Qty, targetAxisItem.AxisGID, targetAxisItem.DatePlanBegin, targetAxisItem.OriginReason, relationType, ref relationList);
            }
            /// <summary>
            /// Z balanceru odebere tolik zdrojů, aby pokryl požadované množství, a vygeneruje přiměřené vztahy.
            /// Vztahy přidává do ref parametru relationList.
            /// Pokud je na vstupu nula nebo menší množství, negeneruje žádný vztah.
            /// Pokud je na vstupu větší množství než je k dispozici, pak v parametru ref qty zůstane nepokryté množství.
            /// Vrací množství, které nebylo možno pokrýt ze zásob balanceru.
            /// Pokud je vše pokryto, vrací 0M.
            /// Odebrané množství vepisuje do položek balanceru (=záznamy osy S) do property QtyConsumed.
            /// </summary>
            /// <param name="qty">Celkové hledané množství</param>
            /// <param name="targetGId">Cílový GID do vztahu</param>
            /// <param name="changeDate">Datum změny (výdeje)</param>
            /// <param name="originReason">Druh výdeje, hledá odpovídající druhy příjmu</param>
            /// <param name="relationType">Druh vztahu, vkládá se do výstupních záznamů</param>
            /// <param name="relationList">Soupis vztahů, kam se nové vztahy přidávají. Pokud je na vstupu null, vytvoří se zde empty.</param>
            /// <returns></returns>
            internal decimal DeQueueQty(decimal qty, GID targetGId, DateTime changeDate, PlanSAxisOriginReason originReason, PlanningItemRelationType relationType, ref List<PlanningItemRelationCls> relationList)
            {
                if (relationList == null) relationList = new List<PlanningItemRelationCls>();

                decimal remainingQty = (qty < 0m ? 0m : qty);
                for (int w = 0; w < 2; w++)
                {	// V cyklu w==0 hledám odpovídající OriginReason, v cyklu w==1 beru každý balancer:
                    if (remainingQty <= 0M) break;
                    for (int i = 0; i < this._BallancerList.Count; i++)
                    {
                        if (remainingQty <= 0M) break;
                        BalItem item = this._BallancerList[i];
                        if (w == 1 || item.OriginReason == PlanSAxisOriginReason.NoReason || ((int)(originReason & item.OriginReason) > 0))
                        {	// V cyklu 1 beru vše, v cyklu 0 beru a) výchozí zásobu nebo b) jen položky, které mají nějakou shodu ve zdroji:
                            decimal qtyUsed = item.GetDisponibleQty(remainingQty);
                            if (qtyUsed > 0M)
                            {
                                relationList.Add(new PlanningItemRelationCls(relationType, item.SourceGId, targetGId, qtyUsed));
                                item.AddUsedQtyAndDate(qtyUsed, changeDate, originReason);
                                remainingQty -= qtyUsed;
                            }
                            if (item.Empty)
                            {	// Balancer je spotřebován?
                                this._BallancerList.RemoveAt(i);
                                i--;
                            }
                            if (remainingQty <= 0M) break;
                        }
                    }
                }
                return remainingQty;
            }
            /// <summary>
            /// Vytvoří vztahy pro nevyužité zůstatky položek balanceru (nevyužitá zásoba).
            /// Pro položky balanceru, které pocházejí z osy S, a mají se zobrazovat v grafu, 
            /// založí navíc další vztah typu PlanningItemRelationType.AxisOutput.
            /// </summary>
            /// <param name="relationType"></param>
            /// <param name="relationList"></param>
            internal void DeQueueRemainder(PlanningItemRelationType relationType, ref List<PlanningItemRelationCls> relationList)
            {
                if (relationList == null)
                    relationList = new List<PlanningItemRelationCls>();

                foreach (BalItem item in this._BallancerList)
                {
                    if (item.SourceGId.IsEmpty) continue;         // Prázdný GID zdroje má počáteční zásoba, tu nebalancuji.
                    if (item.RemainingQty <= 0M) continue;        // Kde není zůstatek, není třeba generovat jeho balancování.

                    // 1. Primární vztah z příjmu do nikam
                    relationList.Add(new PlanningItemRelationCls(relationType, item.SourceGId, GID.Empty, item.RemainingQty));
                    // Vztahy typu Output se zde nevytvářejí, ty se tvoří centrálně v metodě AxisHeapCls.MapFinalRelation()
                }
            }
            private List<BalItem> _BallancerList;
            /// <summary>
            /// Položka balanceru
            /// </summary>
            internal class BalItem
            {
                /// <summary>
                /// Konstruktor.
                /// Tato varianta se používá pro zadání přírůstku zásoby daného příjmem na osu S.
                /// </summary>
                /// <param name="sourceAxisItem"></param>
                internal BalItem(MaterialPlanAxisItemCls sourceAxisItem)
                {
                    this.SourceAxisItem = sourceAxisItem;
                    this.SourceGId = sourceAxisItem.AxisGID;
                    this.SourceQty = sourceAxisItem.Qty;
                    this.UsedQty = 0M;
                    this.OriginReason = sourceAxisItem.OriginReason;
                    this.IssueDateList = new List<DateTime>();
                }
                /// <summary>
                /// Konstruktor.
                /// Tato varianta se používá pro zadání výchozí zásoby ze stavu skladu, bez zadání GIDu záznamu osy S.
                /// </summary>
                /// <param name="sourceQty"></param>
                /// <param name="originReason"></param>
                internal BalItem(decimal sourceQty, PlanSAxisOriginReason originReason)
                {
                    this.SourceGId = GID.Empty;
                    this.SourceQty = sourceQty;
                    this.UsedQty = 0M;
                    this.OriginReason = originReason;
                    this.IssueDateList = new List<DateTime>();
                }
                public override string ToString()
                {
                    return "Source=" + this.SourceGId.ToString() + "; RemainingQty=" + this.RemainingQty.ToString();
                }
                internal bool IsFromAxisS { get { return (this.SourceAxisItem != null); } }
                internal MaterialPlanAxisItemCls SourceAxisItem { get; private set; }
                internal GID SourceGId { get; private set; }
                internal decimal SourceQty { get; private set; }
                internal decimal UsedQty { get; private set; }
                internal PlanSAxisOriginReason OriginReason { get; private set; }
                internal decimal RemainingQty { get { return this.SourceQty - this.UsedQty; } }
                /// <summary>
                /// true, pokud položka je prázdná (její využité množství UsedQty je rovno (nebo větší) jako zdrojové množství SourceQty.
                /// </summary>
                internal bool Empty { get { return this.UsedQty >= this.SourceQty; } }
                /// <summary>
                /// Datumy výdejů. Minimální datum výdeje se přenáší do osy s příjmem z objednávky / výrobního příkazu, pokud není fixované.
                /// </summary>
                internal List<DateTime> IssueDateList { get; private set; }
                /// <summary>
                /// Vrátí disponibilní množství podle množství požadovaného
                /// </summary>
                /// <param name="qtyRequested"></param>
                /// <returns></returns>
                internal decimal GetDisponibleQty(decimal qtyRequested)
                {
                    decimal qtyRemaining = this.RemainingQty;
                    return (qtyRemaining < qtyRequested ? qtyRemaining : qtyRequested);
                }
                /// <summary>
                /// Zapíše si do sebe množství, které bylo odebráno z této položky (= příjem).
                /// Odebrané množství vepisuje do záznamu osy S, do property QtyConsumed.
                /// Pokud zdroj v této položce balanceru pochází z osy (má SourceAxisItem), a důvod záznamu originReason není MinimumStock,
                /// pak do zdrojové osy přidá datum požadavku, aby osa (příjem) věděla, kdy jsou její požadavky.
                /// Umožňuje tak změnit datum příjmu podle potřeb, osa sama má vědět zda je možno datum změnit.
                /// </summary>
                /// <param name="qtyUsed"></param>
                internal void AddUsedQtyAndDate(decimal qtyUsed, DateTime changeDate, PlanSAxisOriginReason originReason)
                {
                    if (qtyUsed > 0M)
                    {
                        this.UsedQty += qtyUsed;
                        if (this.SourceAxisItem != null)
                        {
                            this.SourceAxisItem.QtyConsumed += qtyUsed;

                            if (originReason != PlanSAxisOriginReason.MinimumStock)
                            {
                                this.IssueDateList.Add(changeDate);
                                DateTime minIssueDate = this.IssueDateList.Min();
                                this.SourceAxisItem.SetDateEndFromMinIssue(minIssueDate, originReason);
                            }
                        }
                    }
                }
            }
        }
        #endregion
        #region PODPORA PRO PLÁNOVÁNÍ: SČÍTÁNÍ PRIMÁRNÍCH NÁVRHŮ NA PŘÍJEM DO NÁVRHŮ KUMULOVANÝCH
        /// <summary>
        /// Na základě seznamu prvotních návrhů na příjem vytvoří seznam kumulovaných návrhů.
        /// Aplikuje pravidla kumulace z parametrů procesu a pravodla množství ze skladové karty.
        /// Ze vstupního seznamu nepřenáší dál položky s požadavkem Scheduling.
        /// </summary>
        /// <param name="proposalList">Soupis primárních návrhů ke kumulaci</param>
        /// <param name="findCalendarMethod">Metoda, která umožní najít kalendář podle jeho čísla</param>
        /// <param name="planningParameters">Parametry procesu</param>
        /// <returns></returns>
        internal List<PlanningStockCardProposalCls> CreateCumulativeProposals(List<PlanningStockCardProposalCls> proposalList, FindItem<Calendar> findCalendarMethod, PlanningParametersCls planningParameters)
        {
            List<PlanningStockCardProposalCls> result = new List<PlanningStockCardProposalCls>();

            // Připraví cachované parametry:
            this._CreateProposalsPrepareParams(planningParameters);

            // Vytvořím si pracovní seznam, v němž budu hledat návrhy ke kumulaci, a z něhož budu odebírat hotové záznamy:
            List<PlanningStockCardProposalCls> list = proposalList.FindAll(p => p.Action == StockCardAction.EnsureIncrease);
            list.Sort(PlanningStockCardProposalCls.CompareByTime);

            // Množství, které je navržené navíc nad rámec primárních návrhů 
            //  (z důvodu zarovnání množství z návrhu na minimum od dodavatele nebo na celé násobky dělitele na příjmu):
            // Toto množství se z příštího nalezeného primárního návrhu odečte, 
            //  abychom tento přebytek účelně využili a aby nám nevznikaly sumární přebytky ze "zaokrouhlení":
            // Tato proměnná prochází všemi metodami:
            //  - při tvorbě kumulací se o ni snižuje navrhované množství,
            //  - při určování zarovnaného množství se zvyšuje o přebytky ze zarovnání.
            //  - na konci zdejší metody obsahuje sumární přebytek, který se vyrobí navíc nad požadované množství.
            // Tato proměnná je třídy PlanningProposalOverplus, obsahuje odkaz na návrh z něhož přebytek pochází, a množství které přebývá, 
            //  a které se průběžně snižuje k nule. Proměnná slouží jak k využití přebytku, 
            //  tak k provázání příjmu (návrh) s výdejem (požadavek) = tj. vytvoření vztahu Relation.
            // Kumulace návrhů totiž rovnou zajišťuje přípravu pro balancování vztahů, 
            //  kde do každého připraveného návrhu vkládá vztahy na cílové požadavky, pro které je návrh připraven.
            PlanningProposalOverplus overPlus = new PlanningProposalOverplus();        // Zpracování přebytků z jednoho návrhu do příštích požadavků
            StockCardCumulationMode cumMode = this._GetCurrentCumulationMode();        // Režim kumulací
            int proposalSeq = 0;

            this.AuditProposals(list, "BeforeCumulation, mode=" + cumMode.ToString());

            // Ze seznamu (list) musí každým cyklem nejméně jedna věta zmizet, takže v posledním cyklu zmizí poslední a skončíme:
            while (list.Count > 0)
            {
                int listCnt = list.Count;
                switch (cumMode)
                {
                    case StockCardCumulationMode.None:
                        // Bez kumulací:
                        this._CumulatePropByNone(ref list, ref overPlus, ref result, ref proposalSeq);
                        break;
                    case StockCardCumulationMode.Time:
                        // Kumulace časová prostá:
                        this._CumulatePropByTime(ref list, ref overPlus, ref result, ref proposalSeq);
                        break;
                    case StockCardCumulationMode.Quantity:
                        // Kumulace množstevní:
                        this._CumulatePropByQuantity(ref list, ref overPlus, ref result, ref proposalSeq);
                        break;
                    case StockCardCumulationMode.OptimumBatch:
                        // Kumulace dle optimální dávky:
                        this._CumulatePropByOptimum(ref list, ref overPlus, ref result, ref proposalSeq);
                        break;
                    case StockCardCumulationMode.Calendar:
                        // Kumulace podle kalendáře dodávek:
                        this._CumulatePropByCalendar(ref list, ref overPlus, ref result, ref proposalSeq, findCalendarMethod);
                        break;

                    default:
                        Throw.SysError(MessageInfo.Get("Chyba v procesu kumulace návrhů: byl určen neznámý režimu %0 (%1).", cumMode.ToString(), (int)cumMode));
                        break;
                }
                if (list.Count == listCnt)
                    Throw.SysError(MessageInfo.Get("Chyba v procesu kumulace návrhů: v režimu %0 nebyl zpracován žádný návrh.", cumMode.ToString()));
            }

            this.AuditProposals(result, "CumulationAfter, mode=" + cumMode.ToString());

            // Uvolní z paměti objekt s cachovanými parametry:
            this._StockParams = null;

            return result;
        }
        /// <summary>
        /// Zápis info do auditlogu
        /// </summary>
        /// <param name="info"></param>
        internal void Audit(string info)
        {
            if (!Steward.AppConfig.IsDebug(this.StockUnit.PlanUnitS, this.StockUnit.StockMrp)) return;

            string dateFmt = "dd.MM.yyyy HH:mm";
            string line = "AuditStockCard " + info +
                "; PlanUnitS=" + this.StockUnit.PlanUnitS.ToString() +
                "; Stock=" + this.StockUnit.StockMrp.ToString() +
                "; MinStockQty=" + this.MinStockQty.ToString() +
                "; InitialStockQty=" + this.InitialStockQty.ToString() +
                "; ResultStockQty=" + this.GetResultStockQty().ToString() +
                "; Items.Count=" + this.Items.Count.ToString() +
                "; Level=" + this.Level.ToString() +
                "; PusSL=" + (this.PlanUnitSSl == null ? "null" : this.PlanUnitSSl.RecordNumber.ToString()) +
                "; PusSL.QtyDisp=" + (this.PlanUnitSSl == null ? "null" : this.PlanUnitSSl.QtyDisp.ToString());
            Steward.Audit(AuditRowType.Info, line);
        }
        /// <summary>
        /// Zápis info do auditlogu
        /// </summary>
        /// <param name="proposals"></param>
        /// <param name="info"></param>
        internal void AuditProposals(List<PlanningStockCardProposalCls> proposals, string info)
        {
            if (!Steward.AppConfig.IsDebug(this.StockUnit.PlanUnitS, this.StockUnit.StockMrp)) return;

            foreach (PlanningStockCardProposalCls proposal in proposals)
                proposal.Audit(info);
        }
        /// <summary>
        /// Zajistí vytvoření výsledného návrhu na příjem BEZ KUMULACÍ.
        /// Řeší zarovnání množství / dělení na dávky.
        /// </summary>
        /// <param name="list">Vstupní seznam, je třeba vyřešit záznam na indexu 0 (plus kumulovat vhodné další záznamy)</param>
        /// <param name="overPlus">Průběžný přebytek vzniklý zarovnáním na celého dělitele nebo minimum od dodavatele</param>
        /// <param name="result">Výstupní seznam, do něj se ukládají kumulované návrhy</param>
        /// <param name="proposalSeq">Průběžné číslo návrhu. Po použití se inkrementuje. Na počátku je 0 = číslo prvního návrhu.</param>
        private void _CumulatePropByNone(ref List<PlanningStockCardProposalCls> list, ref PlanningProposalOverplus overPlus, ref List<PlanningStockCardProposalCls> result, ref int proposalSeq)
        {
            PlanningStockCardProposalCls propBase = list[0];
            decimal qtyProp = propBase.GetQtyForBaseProposal(overPlus);    // Určí množství do nového návrhu, využije do něj přebytek od minula, propojí nový návrh s určeným množstvím s jeho primárním požadavkem
            propBase.AddLinkToRequest(propBase, qtyProp);                  // Založí link z návrhu propBase do požadavku z návrhu propBase na dané množství
            if (qtyProp > 0M)
                this._CreateResultCumulativeProposals(propBase, qtyProp, ref overPlus, ref result, ref proposalSeq);  // Upraví množství a založí návrh(y) do výstupního pole

            list.RemoveAt(0);                           // Odeberu výchozí záznam ze vstupního seznamu
        }
        /// <summary>
        /// Zajistí vytvoření výsledného návrhu na příjem s ČASOVOU KUMULACÍ po sobě jdoucích návrhů.
        /// </summary>
        /// <param name="list">Vstupní seznam, je třeba vyřešit záznam na indexu 0 (plus kumulovat vhodné další záznamy)</param>
        /// <param name="overPlus">Průběžný přebytek vzniklý zarovnáním na celého dělitele nebo minimum od dodavatele</param>
        /// <param name="result">Výstupní seznam, do něj se ukládají kumulované návrhy</param>
        /// <param name="proposalSeq">Průběžné číslo návrhu. Po použití se inkrementuje. Na počátku je 0 = číslo prvního návrhu.</param>
        private void _CumulatePropByTime(ref List<PlanningStockCardProposalCls> list, ref PlanningProposalOverplus overPlus, ref List<PlanningStockCardProposalCls> result, ref int proposalSeq)
        {
            // Nastavení procesu:
            TimeSpan cumTime = this._StockParams.CumulationTimePeriod;     // Kumulační časové období
            if (cumTime.TotalMinutes <= 0D)
            {	// Bezpečnostní pojistka:
                this._CumulatePropByNone(ref list, ref overPlus, ref result, ref proposalSeq);
                return;
            }
            decimal pocetMaximum = (this.StavSk.PocetMaximum.IsNull ? 0M : this.StavSk.PocetMaximum.Value);  // Nepřekračovat maximální zásobu

            PlanningStockCardProposalCls propBase = list[0];               // Výchozí návrh, ten bude "kumulován" vždy
            decimal qtyProp = propBase.GetQtyForBaseProposal(overPlus);    // Vrátí množství do nového návrhu, využije do něj přebytek od minula, propojí nový návrh s určeným množstvím s jeho primárním požadavkem
            propBase.AddLinkToRequest(propBase, qtyProp);                  // Založí link z návrhu propBase do požadavku z návrhu propBase na dané množství
            if (qtyProp > 0M)
            {	// Pokud je potřeba nějaké kladné množství (nemusí být, přebytek nám mohl požadované množství zcela zajistit):
                for (int index = 1; index < list.Count; index++)
                {
                    PlanningStockCardProposalCls propTest = list[index];

                    // Pokud tento návrh nelze připojit z hlediska jejich vlastností (kategorie), přeskočíme jej a budeme hledat další:
                    if (!_CreateCumIsCombinable(propBase, propTest)) continue;

                    // Pokud testovaný návrh na indexu [index] je příliš daleko v čase, 
                    //  nebudu jej kumulovat a ani další nebudu testovat (návrhy jsou seřazené podle jejich času, a každý další bude ještě vzdálenější)
                    TimeSpan propTime = propTest.Time - propBase.Time;
                    TimeSpan rounTime = new TimeSpan(propTime.Days, propTime.Hours, propTime.Minutes, 0); // ... zarovnat na celé minuty.
                    if (rounTime >= cumTime) break;

                    // Určím množství z přidávaného návrhu, snížené o předchozí přebytky, pro testovaný návrh:
                    bool addStop;
                    decimal qtyLink;
                    decimal qtyTest = propTest.GetQtyForTestProposal(overPlus, qtyProp, -1M, pocetMaximum, out qtyLink, out addStop);
                    if (addStop) break;          // Tento návrh už neprojde pravidly pro kumulaci (překročil maximální skladovou zásobu).

                    // Zajistíme, aby případný přebytek (linkované množství) z předešlého návrhu byl prolinkován na zdejší požadavek:
                    propTest.LinkOverPlusToThisReq(overPlus, qtyLink);

                    // Založí link z návrhu propBase (příjem) do požadavku z návrhu propTest (výdej) na dané množství:
                    propBase.AddLinkToRequest(propTest, qtyTest);

                    // Testovaný návrh přidám ke kumulovanému návrhu:
                    this._CumulateToBaseProposal(propBase, list, ref index, qtyTest);
                    qtyProp += qtyTest;
                }

                // Upraví množství kumulovaného návrhu a založí návrh(y) do výstupního pole:
                this._CreateResultCumulativeProposals(propBase, qtyProp, ref overPlus, ref result, ref proposalSeq);
            }
            list.RemoveAt(0);              // Odeberu výchozí záznam ze vstupního seznamu
        }
        /// <summary>
        /// Zajistí vytvoření výsledného návrhu na příjem s MNOŽSTEVNÍ KUMULACÍ po sobě jdoucích návrhů.
        /// </summary>
        /// <param name="list">Vstupní seznam, je třeba vyřešit záznam na indexu 0 (plus kumulovat vhodné další záznamy)</param>
        /// <param name="overPlus">Průběžný přebytek vzniklý zarovnáním na celého dělitele nebo minimum od dodavatele</param>
        /// <param name="result">Výstupní seznam, do něj se ukládají kumulované návrhy</param>
        /// <param name="proposalSeq">Průběžné číslo návrhu. Po použití se inkrementuje. Na počátku je 0 = číslo prvního návrhu.</param>
        private void _CumulatePropByQuantity(ref List<PlanningStockCardProposalCls> list, ref PlanningProposalOverplus overPlus, ref List<PlanningStockCardProposalCls> result, ref int proposalSeq)
        {
            // Nastavení procesu:
            decimal cumQtty = this._StockParams.QtyMinDod;       // Strop množství do množstevní kumuly = minimum od dodavatele
            if (cumQtty <= 0)
            {	// Bezpečnostní pojistka:
                this._CumulatePropByNone(ref list, ref overPlus, ref result, ref proposalSeq);
                return;
            }
            decimal pocetMaximum = (this.StavSk.PocetMaximum.IsNull ? 0M : this.StavSk.PocetMaximum.Value);  // Nepřekračovat maximální zásobu

            PlanningStockCardProposalCls propBase = list[0];               // Výchozí návrh, ten bude "kumulován" vždy
            decimal qtyProp = propBase.GetQtyForBaseProposal(overPlus);    // Vrátí množství do nového návrhu, využije do něj přebytek od minula, propojí nový návrh s určeným množstvím s jeho primárním požadavkem
            propBase.AddLinkToRequest(propBase, qtyProp);                  // Založí link z návrhu propBase do požadavku z návrhu propBase na dané množství
            if (qtyProp > 0M)
            {	// Pokud je potřeba nějaké kladné množství (nemusí být, přebytek nám mohl požadované množství zcela zajistit):
                for (int index = 1; index < list.Count; index++)
                {
                    PlanningStockCardProposalCls propTest = list[index];

                    // Pokud tento návrh nelze připojit z hlediska jejich vlastností (kategorie), budeme hledat další:
                    if (!_CreateCumIsCombinable(propBase, propTest)) continue;

                    // Určím množství z přidávaného návrhu, snížené o předchozí přebytky, pro testovaný návrh:
                    bool addStop;
                    decimal qtyLink;
                    decimal qtyTest = propTest.GetQtyForTestProposal(overPlus, qtyProp, cumQtty, pocetMaximum, out qtyLink, out addStop);
                    if (addStop) break;          // Tento návrh už neprojde pravidly pro kumulaci (překročil maximální skladovou zásobu / kumulační množství).

                    // Zajistíme, aby případný přebytek (linkované množství) z předešlého návrhu byl prolinkován na zdejší požadavek:
                    propTest.LinkOverPlusToThisReq(overPlus, qtyLink);

                    // Založí link z návrhu propBase (příjem) do požadavku z návrhu propTest (výdej) na dané množství:
                    propBase.AddLinkToRequest(propTest, qtyTest);

                    // Testovaný návrh přidám ke kumulaci:
                    this._CumulateToBaseProposal(propBase, list, ref index, qtyTest);
                    qtyProp += qtyTest;
                }

                // Upraví množství kumulovaného návrhu a založí návrh(y) do výstupního pole:
                this._CreateResultCumulativeProposals(propBase, qtyProp, ref overPlus, ref result, ref proposalSeq);
            }

            list.RemoveAt(0);              // Odeberu výchozí záznam ze vstupního seznamu
        }
        /// <summary>
        /// Zajistí vytvoření výsledného návrhu na příjem s OPTIMÁLNÍ KUMULACÍ (podle maximální dávky) po sobě jdoucích návrhů.
        /// </summary>
        /// <param name="list">Vstupní seznam, je třeba vyřešit záznam na indexu 0 (plus kumulovat vhodné další záznamy)</param>
        /// <param name="overPlus">Průběžný přebytek vzniklý zarovnáním na celého dělitele nebo minimum od dodavatele</param>
        /// <param name="result">Výstupní seznam, do něj se ukládají kumulované návrhy</param>
        /// <param name="proposalSeq">Průběžné číslo návrhu. Po použití se inkrementuje. Na počátku je 0 = číslo prvního návrhu.</param>
        private void _CumulatePropByOptimum(ref List<PlanningStockCardProposalCls> list, ref PlanningProposalOverplus overPlus, ref List<PlanningStockCardProposalCls> result, ref int proposalSeq)
        {
            if (this.StavSkMaximalniDavka.IsNull || this.StavSkMaximalniDavka.Value <= 0M)
            {	// Bezpečnostní pojistka:
                this._CumulatePropByNone(ref list, ref overPlus, ref result, ref proposalSeq);
                return;
            }

            // Nastavení procesu:
            decimal cumOpti = this._StockParams.QtyOptimumBatchProposal; // Optimální dávka pro běžné návrhy, zarovnaná (minumum, dělitel)
            decimal pocetMaximum = this._StockParams.QtyMaximum;         // Nepřekračovat maximální zásobu

            PlanningStockCardProposalCls propBase = list[0];                       // Výchozí návrh, ten bude "kumulován" vždy
            decimal qtyProp = propBase.GetQtyForBaseProposal(overPlus);            // Vrátí množství do nového návrhu, využije do něj přebytek od minula, propojí nový návrh s určeným množstvím s jeho primárním požadavkem
            propBase.AddLinkToRequest(propBase, qtyProp);                          // Založí link z návrhu propBase do požadavku z návrhu propBase na dané množství

            #region Komentáře
            //    Několik slov k přebytku (overPlus) v tomto režimu:
            // a) V této metodě by se neměl vyskytovat, protože se generují návrhy na zarovnané počty
            //     (to je fakt, hledej důvody okolo proměnné StockParamQtyOptimumBatchProposal a metody _FindQtyListForProposal())
            // b) I když by se nakrásně přebytek na vstupu objevil (v parametru se čistě teoreticky může objevit hodnota overPlus.Qty > 0M),
            //     pak se nikdy nedostanu do problémů uvnitř bloku if (qtyUnder > 0M) { ... }, protože:
            //  b1. Pokud bude přebytek overPlus == 0, pak v něm nula zůstane a qtyProp == propBase.QtyProposalRemaining
            //  b2. Pokud bude přebytek overPlus menší, než je množství v požadavku propBase.QtyProposalRemaining, 
            //       pak se přebytek (overPlus) vynuluje a množství qtyProp se o něj sníží
            //        (qtyProp = (propBase.QtyProposalRemaining - overPlus)
            //  b3. Pokud bude přebytek overPlus shodný, pak se vynuluje jak overPlus, tak výsledek qtyProp
            //  b4. Pokud bude přebytek overPlus větší než požadavek propBase.QtyProposalRemaining,
            //       pak se přebytek overPlus sníží o propBase.QtyProposalRemaining, návrh propBase bude celý pokrytý tímto přebytkem,
            //       v přebytku overPlus zůstane kladné číslo, a návrh qtyProp bude 0.
            // c) V žádné větvi se nedostáváme k tomu, že by qtyProp bylo větší než 0 a současně i overPlus je větší než 0.
            //       On to je i logický nesmysl: proč bychom si drželi nespotřebovaný přebytek, když bychom měli nějaký požadavek?
            // d) Výsledkem je, že pokud je nějaký kladný požadavek, nemusím se starat o přebytek.
            //     Platí to jen v této metodě kumulace!!!
            //     A to proto, že tady hledáme další návrhy ke kumulaci jen tehdy, 
            //      když potřebné množství pro prvotní návrh je větší než 0 (pak musí být přebytek == 0).


            // Jak funguje tato kumulace:
            // K prvotnímu návrhu přidává množství z dalších kumulovatelných návrhů tak dlouho, 
            //  dokud součet nedosáhne optimální dávky (cumOpti).
            // Pak tuto skupinu uvolní do kumulovaného návrhu (do výroby / objednání).
            // Jakmile součet množství přesáhne optimální dávku, přesahující množství do aktuální dávky nebude přidávat,
            //  ponechá jej v primárním návrhu, a svoji dávku uzavře.
            // Pokud sám prvotní návrh je zarovnaný na optimální dávku, pustí se do výroby samotný bez další kumulace.
            // V primárním návrhu je property QtyProposalRemaining, která zohledňuje částečně vyřízené množství (QtyProposalPrepared).

            // Hlídání maximální zásoby skladu:
            // Výchozí návrh ji sám o sobě překročit může (protože musí, musíme prvotní návrh = poptávku pokrýt),
            //  ale kumulace s dalšími návrhy ji překročit nesmí.
            // Příklad: mějme zásobu na stavu skladu = 40, minimální zásobu = 20, maximální stav = 500, výrobní dávku = 100
            // Za této situace existuje požadavek na výdej (poptávka) na množství 475.
            // Připraví se primární návrh na příjem tak, aby po výdeji 475 ks zůstala minimální zásoba:
            // QtyProp : (40 - 475 + QtyProp) = 20   =>   QtyProp = 20 - 40 + 475 = 455
            // Z hlediska skladu bude vývoj zásoby: 40 + 455 = 495, 495 - 475 = 20. 
            // Maximální zásobu nepřekročíme.
            // Z hlediska optimální dávky a kumulací pak pracujeme zde takto:
            // Optimální výrobní dávka = 100, doporučí se dávky: QtyProp = 475, dávky 5 x 100 = 500
            // Vypočte se qtyUnder = 25 (500 - 475) = množství, které by bylo možno kumulovat, 
            // zde dohledáváme další primární návrhy abychom těchto 25 ks doplnili.
            // Ale při tom musíme hlídat maximální zásobu skladu!
            // Aby poslední dávka nepřesáhla kapacitu skladu, musíme ji zde snížit, a teprve tuto sníženou hranici plnit:
            #endregion

            decimal qtyUnder = this._CumulateOptimalGetQtyUnder(qtyProp); // Množství, které chybí v poslední dávce do jejího plného počtu
            bool cutByMax = (pocetMaximum > 0M && (propBase.PreviousQty + qtyProp + qtyUnder) > pocetMaximum);
            if (cutByMax)
                qtyUnder = pocetMaximum - (propBase.PreviousQty + qtyProp);

            if (qtyUnder > 0M)
            {
                // Pokud výchozí návrh na příjem má takové množství, že jeho poslední výrobní dávka nebude ucelená, 
                //  najdeme k ní další návrhy, aby se ucelila:

                if (overPlus.Qty > 0M)
                    // Viz komentář v bloku o 60 řádků výše: přebytek musí být == 0, jinak je něco špatně:
                    Throw.SysError(MessageInfo.Get("Chyba algoritmu StockCardCls._CumulatePropByOptimum(): hledá návrhy na doplnění dávky, a přitom má kladný přebytek overPlus."));

                for (int index = 1; index < list.Count; index++)
                {
                    PlanningStockCardProposalCls propTest = list[index];

                    // Pokud tento návrh nelze připojit z hlediska jejich vlastností (kategorie), budeme hledat další:
                    if (!_CreateCumIsCombinable(propBase, propTest)) continue;

                    // Kolik z tohoto návrhu (z jeho zbývajícího množství) můžu přidat do našeho výchozího návrhu, 
                    // abych ucelil množství jeho poslední dávky?
                    decimal qtyTest = propTest.QtyProposalRemaining;
                    if (qtyTest > qtyUnder)
                    {	// Nalezený návrh je na množství větší, než potřebujeme na zarovnání poslední výrobní dávky:
                        if (cutByMax)
                            // Pokud jsme omezeni maximální zásobou na skladu, pak z tohoto návrhu nepřidáme nic:
                            qtyTest = 0M;
                        else
                            // Pokud jsme omezeni velikostí poslední dávky, pak přidáme co se do ní vejde:
                            qtyTest = qtyUnder;

                        // Tímto jsme limit poslední dávky vyčerpali a další návrhy už nebudeme přibírat:
                        qtyUnder = 0M;
                    }
                    else
                    {	// Návrh je na množství stejné nebo menší, tímto se zcela vyřídí (přidá se do kumulace):
                        // Limit poslední dávky snížíme o zdejší množství:
                        qtyUnder -= qtyTest;
                    }

                    // Budeme z návrhu propTest něco přidávat do kumulace?
                    if (qtyTest > 0M)
                    {
                        // Založí link z návrhu propBase (příjem) do požadavku z návrhu propTest (výdej) na dané množství:
                        propBase.AddLinkToRequest(propTest, qtyTest);

                        this._CumulateToBaseProposal(propBase, list, ref index, qtyTest);
                        qtyProp += qtyTest;
                    }

                    // Už je hotovo?
                    if (qtyUnder <= 0M)
                        break;
                }
            }

            // Upraví množství (zde se fyzicky rozdělí na jednotlivé dávky) a založí návrh(y) do výstupního pole:
            this._CreateResultCumulativeProposals(propBase, qtyProp, ref overPlus, ref result, ref proposalSeq);

            list.RemoveAt(0);              // Odeberu výchozí záznam ze vstupního seznamu
        }
        /// <summary>
        /// Zajistí vytvoření výsledného návrhu na příjem s KUMULACÍ PODLE KALENDÁŘE DODÁVEK
        /// </summary>
        /// <param name="list">Vstupní seznam, je třeba vyřešit záznam na indexu 0 (plus kumulovat vhodné další záznamy)</param>
        /// <param name="overPlus">Průběžný přebytek vzniklý zarovnáním na celého dělitele nebo minimum od dodavatele</param>
        /// <param name="result">Výstupní seznam, do něj se ukládají kumulované návrhy</param>
        /// <param name="proposalSeq">Průběžné číslo návrhu. Po použití se inkrementuje. Na počátku je 0 = číslo prvního návrhu.</param>
        /// <param name="findCalendarMethod">Metoda, která umožní najít kalendář podle jeho čísla</param>
        private void _CumulatePropByCalendar(ref List<PlanningStockCardProposalCls> list, ref PlanningProposalOverplus overPlus, ref List<PlanningStockCardProposalCls> result, ref int proposalSeq, FindItem<Calendar> findCalendarMethod)
        {
            // Parametry procesu = kalendář:
            int calendarKey = (this.StavSk.KalendarDodavek.IsNull ? 0 : this.StavSk.KalendarDodavek.Value);
            Calendar calendar = (calendarKey == 0 || findCalendarMethod == null ? null : findCalendarMethod(calendarKey));
            if (calendar == null || calendar.IsEmpty)
            {	// Bezpečnostní pojistka:
                this._CumulatePropByNone(ref list, ref overPlus, ref result, ref proposalSeq);
                return;
            }
            decimal pocetMaximum = (this.StavSk.PocetMaximum.IsNull ? 0M : this.StavSk.PocetMaximum.Value);  // Nepřekračovat maximální zásobu

            PlanningStockCardProposalCls propBase = list[0];               // Výchozí návrh, ten bude "kumulován" vždy
            propBase.Time = this._CumulateCalendarGetDate(propBase.Time, TimeRange.TimeDirection.ToHistory, calendar); // Datum návrhu dle kalendáře, na celé minuty
            decimal qtyProp = propBase.GetQtyForBaseProposal(overPlus);    // Vrátí množství do nového návrhu, využije do něj přebytek od minula, propojí nový návrh s určeným množstvím s jeho primárním požadavkem
            propBase.AddLinkToRequest(propBase, qtyProp);                  // Založí link z návrhu propBase do požadavku z návrhu propBase na dané množství
            if (qtyProp > 0M)
            {	// Pokud je potřeba nějaké kladné množství (nemusí být, přebytek nám mohl požadované množství zcela zajistit):
                for (int index = 1; index < list.Count; index++)
                {
                    PlanningStockCardProposalCls propTest = list[index];

                    // Pokud tento návrh nelze připojit z hlediska jejich vlastností (kategorie), přeskočíme jej a budeme hledat další:
                    if (!_CreateCumIsCombinable(propBase, propTest)) continue;

                    // Zjistíme, zda vhodný termín dodávky pro testovaný návrh lze vyřídit v termínu základního návrhu:
                    bool timeTest = this._CumulateCalendarTestDate(propTest.Time, TimeRange.TimeDirection.ToHistory, calendar, propBase.Time);
                    if (!timeTest) break;

                    // Určím množství z přidávaného návrhu, snížené o předchozí přebytky, pro testovaný návrh:
                    bool addStop;
                    decimal qtyLink;
                    decimal qtyTest = propTest.GetQtyForTestProposal(overPlus, qtyProp, -1M, pocetMaximum, out qtyLink, out addStop);
                    if (addStop) break;          // Tento návrh už neprojde pravidly pro kumulaci (překročil maximální skladovou zásobu).

                    // Zajistíme, aby případný přebytek (linkované množství) z předešlého návrhu byl prolinkován na zdejší požadavek:
                    propTest.LinkOverPlusToThisReq(overPlus, qtyLink);

                    // Založí link z návrhu propBase (příjem) do požadavku z návrhu propTest (výdej) na dané množství:
                    propBase.AddLinkToRequest(propTest, qtyTest);

                    // Testovaný návrh přidám ke kumulovanému návrhu:
                    this._CumulateToBaseProposal(propBase, list, ref index, qtyTest);
                    qtyProp += qtyTest;
                }

                // Upraví množství kumulovaného návrhu a založí návrh(y) do výstupního pole:
                this._CreateResultCumulativeProposals(propBase, qtyProp, ref overPlus, ref result, ref proposalSeq);
            }
            list.RemoveAt(0);              // Odeberu výchozí záznam ze vstupního seznamu
        }
        /// <summary>
        /// Určí datum, kdy je aktivní některý úsek podle daného kalendáře.
        /// V daném kalendáři najde nejbližší čas, v daném směru, od daného data, kdy "se pracuje".
        /// Výsledný čas zarovná na celé sekundy.
        /// </summary>
        /// <param name="dateTime">Potřebné datum</param>
        /// <param name="timeDir">Směr hledání</param>
        /// <param name="calendar">Objekt kalendáře, který určuje termíny</param>
        /// <returns></returns>
        private DateTime _CumulateCalendarGetDate(DateTime dateTime, TimeRange.TimeDirection timeDir, Calendar calendar)
        {
            // Zeptáme se kalendáře na nejbližší interval podle jeho vnitřních dat, který vyhovuje danému datu a času:
            //  například: 
            //    - v kalendáři je položka "Pondělí 12:00 - 12:15", 
            //    - my se ptáme na středu 16.3.2011 čas 14:45, směr ToHistory,
            //    - kalendář vrátí interval { 14.3.2011 12:00 - 12:15 }, což je nejbližší pondělní interval před hledaným časem:
            TimeRange calTime = calendar.FindNearestInterval(dateTime, timeDir, true);
            if (calTime.IsEmpty) return dateTime;       // Nejsou data => vracíme požadavek beze změny.

            // Určíme výsledné datum:
            //  pokud je hledané datum uvnitř nalezeného intervalu, pak výsledkem je přímo hledané datum:
            if (calTime.Contain(dateTime))
                return dateTime;

            // Nalezený interval je jinde, než hledané datum: vrátíme přiměřený okraj intervalu:
            return calTime.Edge(timeDir, true);
        }
        /// <summary>
        /// Zjistí, zda lze kumulovat dva návrhy podle jejich termínu dodání, který je určen kalendářem.
        /// Pro datum testovaného primárního návrhu určí vhodný datum (a interval) dodání - podle kalendáře,
        /// a ověří, zda jde o týž interval, který je určen pro základní návrh.
        /// </summary>
        /// <param name="dateTime">Datum testovaného návrhu, primární, bez úprav</param>
        /// <param name="timeDir">Směr hledání</param>
        /// <param name="calendar">Objekt kalendáře, který určuje termíny</param>
        /// <param name="baseDate">Datum základnového návrhu, po úpravě kalendářem</param>
        /// <returns></returns>
        private bool _CumulateCalendarTestDate(DateTime dateTime, TimeRange.TimeDirection timeDir, Calendar calendar, DateTime baseDate)
        {
            if (dateTime < baseDate)
                Throw.SysError(MessageInfo.Get("Chyba v postupu kumulace návrhu: datum základnového návrhu je vyšší, než datum testovaného návrhu."));

            TimeRange calTime = calendar.FindNearestInterval(dateTime, timeDir, true);
            if (calTime.IsEmpty) return false;       // Nejsou data => vracíme false (nekumulovat).

            // Pokud kalendář vrátil takový interval, ve kterém leží i základnový návrh, pak lze návrhy vyřídit jednou dodávkou:
            return calTime.Contain(baseDate);
        }
        /// <summary>
        /// Vrátí množství, které chybí do ucelené dávky.
        /// Na vstupu je množství návrhu, metoda určí zarovnaný počet při zachování celých dávek, 
        /// a vrátí množství které chybí do této dávky.
        /// Metoda pracuje s dávkou o velikosti this.StockParamQtyOptimumBatchProposal.
        /// Příklad: při dávce 100 a zadaném množství 365 se vrátí 35 (365 + 35 = 4x 100).
        /// Při dávce 100 a zadaném množství 200 se vrátí 0 (do ucelené dávky nic nechybí).
        /// Metoda vrací 0 pokud je na vstupu 0 nebo není definovaná optimální dávka.
        /// </summary>
        /// <param name="qtyProp">Vstupní množství</param>
        /// <returns>Chybějící množství do ucelené dávky</returns>
        private decimal _CumulateOptimalGetQtyUnder(decimal qtyProp)
        {
            if (qtyProp < 0M) return 0M;
            decimal qtyBatch = this._StockParams.QtyOptimumBatchProposal;
            if (qtyBatch <= 0M) return 0M;                         // Pokud není zadaná dávka, nic do ní nechybí.
            decimal batchCount = Math.Ceiling(qtyProp / qtyBatch); // Počet celých dávek, zarovnaný na celé číslo nahoru
            return ((batchCount * qtyBatch) - qtyProp);            // Kolik chybí do ucelené dávky?
        }
        /// <summary>
        /// Zajistí kumulaci dat dvou návrhů. Kumulované množství dostává explicitně určené (to se určuje jinde).
        /// Provede tedy shrnutí dat o návrzích (OriginReason, Author, příznak ProposalIsCombined).
        /// Kumulované množství zapíše do zdrojového návrhu (na indexu index) jako vyřízené (přičte jej k QtyProposalPrepared).
        /// Pokud tento návrh je tímto vyřízen zcela, pak jej odebere ze vstupního pole (list) a sníží o 1 ref index.
        /// </summary>
        /// <param name="propBase">Základní primární návrh</param>
        /// <param name="list">Seznam vstupních návrhů, v něm najdeme přidávaný návrh, a případně jej odebereme</param>
        /// <param name="index">Inp: Index, na kterém je přidávaný návrh, pokud se odebere bude o 1 snížen</param>
        /// <param name="qtySum">Množství, které se z tohoto návrhu vyřizuje kumulací.</param>
        private bool _CumulateToBaseProposal(PlanningStockCardProposalCls propBase, List<PlanningStockCardProposalCls> list, ref int index, decimal qtySum)
        {
            bool result = false;
            PlanningStockCardProposalCls propAdd = list[index];      // Kumulovaný návrh (ten přidávaný, ne ten základní)

            // 1. V základním návrhu upraví info o původu návrhu kumulací s přidávaným návrhem (např. zruší autora):
            propBase.CumulateWithProposal(propAdd, qtySum);

            // 2. Navýšit výchozí zásobu (PreviousQty) pro všechny návrhy ležící mezi 
            //    indexem 1 (tam začne mít přesunutý návrh na příjem efekt) 
            //    a (index) (což je tento) ještě bude přesunem ovlivněn (to je potřeba, protože jej možná nebudu vyřazovat ze seznamu a zůstane tam na příště),
            //    protože jejich výchozí zásoba dosud počítala s tímto příjmem až později:
            for (int i = 1; i <= index; i++)
                list[i].PreviousQty += qtySum;

            // 3. Množství ke kumulaci přidám do množství vyřízeného na přidávanému návrhu:
            propAdd.QtyProposalPrepared += qtySum;

            // 4. Pokud je přidávaný návrh přidán celý (nezbývá žádné množství do návrhu), odebere se ze vstupního seznamu, a sníží index:
            if (propAdd.QtyProposalRemaining <= 0M)
            {
                list.RemoveAt(index);
                index--;
                result = true;
            }
            return result;
        }
        /// <summary>
        /// Do výsledného seznamu přidá návrh(y) na příjem vytvořené na základě požadovaného množství.
        /// Požadované množství nejprve koriguje podle pravidel skladu (zarovná na Max, Round), rozdělí (na dávky).
        /// Do seznamu přidává klony vstupního záznamu návrhu (proposal), kde každý klon obsahuje přiměřené množství.
        /// </summary>
        /// <param name="proposal">Bázový návrh, pro který je zakládán výsledný kumulovaný návrh</param>
        /// <param name="qtyProp">Množství návrhu požadované</param>
        /// <param name="overPlus">Průběžný přebytek vzniklý zarovnáním na celého dělitele nebo minimum od dodavatele</param>
        /// <param name="result">Výstupní seznam, do něj se ukládají kumulované návrhy</param>
        /// <param name="proposalSeq">Průběžné číslo návrhu. Po použití se inkrementuje. Na počátku je 0 = číslo prvního návrhu.</param>
        private void _CreateResultCumulativeProposals(PlanningStockCardProposalCls proposal, decimal qtyProp, ref PlanningProposalOverplus overPlus, ref List<PlanningStockCardProposalCls> result, ref int proposalSeq)
        {
            if (qtyProp > 0M)
            {	// Pokud kumulované množství je kladné (nemusí to být vždy: 
                //  když dělám jednotlivý návrh, a zajistím jeho požadavek přebytkem z minulých návrhů, pak qtyProp = 0)
                // Rozdělím dané množství na maximální výrobní dávky, zarovnám je na minimum od dodavatele a na dělitele na příjmu:
                decimal overPlusQty = overPlus.Qty;         // overPlusQty obsahuje přebytek vzniklý zarovnáním množství
                List<decimal> qtyList = this._FindQtyListForProposal(qtyProp, proposal.ProposalForMinQty, ref overPlusQty);

                // Jednotlivé (maximální) dávky:
                proposalSeq++;
                int proposalSub = 0;
                proposal.CreatePieceLinkInit();
                foreach (decimal qtyOne in qtyList)
                    // Každé jednotlivé množství: přidám k výsledku nový návrh s tímto množstvím:
                    result.Add(proposal.CreatePieceProposalForQty(qtyOne, proposalSeq, ref proposalSub));

                // Aktualizace dat o přebytku (overPlus): jako jeho Proposal bude figurovat poslední návrh v resultu, množství = overPlusQty.
                // V resultu bude přinejmenším jeden záznam, protože :
                // množství v této větvi je kladné => seznam qtyList má přinejmenším jeden prvek => result rovněž:
                if (result.Count > 0 && overPlusQty > 0M)
                    overPlus.SetNewOverplus(result[result.Count - 1], overPlusQty);
                else if (overPlusQty == 0M)
                    overPlus.SetNewOverplus(null, overPlusQty);
            }
        }
        /// <summary>
        /// Vrátí aktuální režim kumulací na této kartě.
        /// Zohlední požadavek kmenové karty, stav skladu a povolení optimálních dávek.
        /// </summary>
        /// <returns></returns>
        private StockCardCumulationMode _GetCurrentCumulationMode()
        {
            // Podle režimu kumulací na této skladové kartě (režim se přebírá z kmenové karty):
            StockCardCumulationMode cumulationMode = this.CumulationMode;
            // Režim "Časový" má přednost před Optimální dávkou.
            // Pokud není režim "Časový", a jsou splněny podmínky pro aplikaci kumulace režimem Optimální dávka, použije se tento režim.
            if (cumulationMode != StockCardCumulationMode.Time && this._StockParams.AcceptOptimumBatch && !this.StavSkMaximalniDavka.IsNull && this.StavSkMaximalniDavka.Value > 0M)
                cumulationMode = StockCardCumulationMode.OptimumBatch;

            // Pokud je zadán režim "Časová kumulace", a na stavu skladu je uveden kalendář dodávek, pak běží kumulace ke dni dodávky:
            if (cumulationMode == StockCardCumulationMode.Time && !this.StavSk.KalendarDodavek.IsNull)
                cumulationMode = StockCardCumulationMode.Calendar;

            if (cumulationMode == StockCardCumulationMode.OptimumBatch && (this.StavSkMaximalniDavka.IsNull || this.StavSkMaximalniDavka.Value <= 0M))
                // Defenzivní postup: pokud by na začátku bylo nastaveno chybně this.CumulationMode == StockCardCumulationMode.OptimumBatch,
                //  a přitom by this.StavSkMaximalniDavka == DbNull, pak nelze tuto kumulaci použít (došlo by k chybě):
                cumulationMode = StockCardCumulationMode.None;

            if (cumulationMode == StockCardCumulationMode.Quantity && this.StavSkMinimumDodavatel <= 0M)
                // Pokud je požadována množstevní kumulace, ale není zadané množství, pak to nelze provést:
                cumulationMode = StockCardCumulationMode.None;

            return cumulationMode;
        }
        /// <summary>
        /// Zjistí, zda dva návrhy lze vyřešit vzájemnou kumulací.
        /// Tato metoda neřeší množství ani čas, ale charakter návrhů a parametry procesu.
        /// </summary>
        /// <param name="propBase">Základní návrh na příjem</param>
        /// <param name="propTest">Testovaný návrh na příjem</param>
        /// <returns></returns>
        private bool _CreateCumIsCombinable(PlanningStockCardProposalCls propBase, PlanningStockCardProposalCls propTest)
        {
            // Pokud jsou to dva návrhy MinQty, tak ty kumulovat lze vždycky:
            if (propBase.ProposalForMinQty && propTest.ProposalForMinQty) return true;

            // Pokud se nepracuje s kategoriemi, lze návrhy kumulovat vždy (kumulaci MinQty a Real řešíme jinde: this._CreateCumSumProposals()):
            if (!this._StockParams.WorkWithCategories) return true;

            // Pokud se pracuje s kategoriemi, pak nelze kombinovat návrhy MinQty s návrhy k reálnému požadavku:
            if (propBase.ProposalForMinQty != propTest.ProposalForMinQty) return false;

            // A navíc oba návrhy musí být k reálnému požadavku, jinak je nejde sčítat:
            if (propBase.AxisItemRequest == null || propTest.AxisItemRequest == null) return false;

            // Pracuje se s kategoriemi => porovnám důvody vzniku obou návrhů a vrátím true, když jsou oba návrhy ze stejného důvodu:
            return (propBase.AxisItemRequest.OriginReason == propTest.AxisItemRequest.OriginReason);
        }
        #endregion
        #region URČENÍ MNOŽSTVÍ DO PŘIPRAVOVANÉHO NÁVRHU - VČETNĚ DĚLENÍ NA OPTIMÁLNÍ DÁVKY
        /// <summary>
        /// Metoda určí množství pro jednotlivé návrhy, při požadavku na příjem daného množství.
        /// Metoda generuje seznam hodnot Qty, které vyhovují vlastnostem skladové karty, 
        /// a jejichž součet je rovný nebo vyšší zadanému množství.
        /// Zde se zohlední jak nejmenší množství od dodavatele, tak optimální dávka.
        /// </summary>
        /// <param name="args">Data pro určené množství v jednotlivých dávkách</param>
        /// <returns>Seznam údajů o množství v jednotlivých dávkách</returns>
        internal List<decimal> FindQtyListForProposal(PlanningStockCardProposalCls args)
        {
            decimal overPlusQty = 0M;
            return this._FindQtyListForProposal(args.Qty, false, ref overPlusQty);
        }
        /// <summary>
        /// Metoda určí množství pro jednotlivé návrhy, při požadavku na příjem daného množství.
        /// Metoda generuje seznam hodnot Qty, které vyhovují vlastnostem skladové karty, 
        /// a jejichž součet je rovný nebo vyšší zadanému množství.
        /// Zde se zohlední jak nejmenší množství od dodavatele, tak optimální dávka.
        /// </summary>
        /// <param name="qty">Požadované množství</param>
        /// <returns>Seznam údajů o množství v jednotlivých dávkách</returns>
        internal List<decimal> FindQtyListForProposal(decimal qty)
        {
            decimal overPlusQty = 0M;
            return this._FindQtyListForProposal(qty, false, ref overPlusQty);
        }
        /// <summary>
        /// Metoda určí množství pro jednotlivé návrhy, při požadavku na příjem daného množství.
        /// Metoda generuje seznam hodnot Qty, které vyhovují vlastnostem skladové karty, 
        /// a jejichž součet je rovný nebo vyšší zadanému množství.
        /// Zde se zohlední jak nejmenší množství od dodavatele, tak optimální dávka.
        /// </summary>
        /// <param name="qty">Požadované množství</param>
        /// <param name="planningParameters">Parametry, podle kterých se bude přihlížet k jednotlivým atributům na skladové kartě</param>
        /// <returns>Seznam údajů o množství v jednotlivých dávkách</returns>
        internal List<decimal> FindQtyListForProposal(decimal qty, PlanningParametersCls planningParameters)
        {
            decimal overPlusQty = 0M;
            return this._FindQtyListForProposal(qty, false, ref overPlusQty);
        }
        /// <summary>
        /// Metoda určí množství pro jednotlivé návrhy, při požadavku na příjem daného množství.
        /// Metoda generuje seznam hodnot Qty, které vyhovují vlastnostem skladové karty, 
        /// a jejichž součet je rovný nebo vyšší zadanému množství.
        /// Zde se zohlední jak nejmenší množství od dodavatele, tak optimální dávka.
        /// Současně se aktualizuje přebývající množství (parametr ref overPlus).
        /// </summary>
        /// <param name="qty">Požadované množství</param>
        /// <param name="isForMinQty">Jde o návrh z titulu minimální zásoby? Pro něj se zohledňuje jiné nastavení (planningParameters.MrpMinDodToMinQty)</param>
        /// <param name="overPlus">Průběžný přebytek množství ze zarovnání</param>
        /// <returns>Seznam obsahující množství v jednotlivých dávkách</returns>
        private List<decimal> _FindQtyListForProposal(decimal qty, bool isForMinQty, ref decimal overPlusQty)
        {
            // Soupis jednotlivých maximálních výrobních dávek = výsledek metody:
            List<decimal> result = new List<decimal>();

            decimal optimumBatch = this._StockParams.QtyOptimumBatch;
            decimal remainingQty = qty;
            decimal realQty = 0M;             // Reálný součet ze všech připravených množství
            while (remainingQty > 0M)
            {
                // Množství do jednoho návrhu na příjem: buď vše zbývající, anebo optimální dávka:
                decimal oneQty = (optimumBatch <= 0M || remainingQty < optimumBatch ? remainingQty : optimumBatch);

                // Úprava množství jedné dávky podle minima od dodavatele, podle dělitele množství na příjmu:
                oneQty = GetQtyAdjustedByStock(oneQty, isForMinQty);

                // Určené množství jedné dávky do výstupu, a odečíst od počitadla množství dosud nezpracovaného:
                result.Add(oneQty);
                realQty += oneQty;
                remainingQty -= oneQty;
            }
            // Pokud jsme navrhli více, než bylo požadováno,
            //   pak ten přebytek přičteme k overPlus (přebytečné množství, vypořádává se někde jinde):
            if (realQty > qty)
                overPlusQty += (realQty - qty);

            return result;
        }
        /// <summary>
        /// Metoda vrátí dané množství ošetřené podle pravidel skladu (kromě dělení na dávky).
        /// Metoda aplikuje minimum od dodavatele a zarovnání pomocí dělitele množství na příjmu.
        /// </summary>
        /// <param name="qty">Požadované množství</param>
        /// <param name="isForMinQty">Zarovnat pro minimální množství (true) / pro běžný návrh (false)</param>
        /// <returns></returns>
        internal decimal GetQtyAdjustedByStock(decimal qty, bool isForMinQty)
        {
            return this._StockParams.GetQtyAdjustedByStock(qty, isForMinQty);
        }
        /// <summary>
        /// Vrátí danou hodnotu zaokrouhlenou daným dělitelem.
        /// Směr zaokrouhlení se určuje parametrem "roundUp", přičemž true = nahoru = na vyšší čísla (pro záporné hodnoty = na menší hodnotu).
        /// Příklady:
        /// (21, 5, true) = 25;
        /// (-32, 6, true) = -36;
        /// (-32, 6, false) = -30;
        /// </summary>
        /// <param name="value"></param>
        /// <param name="divider"></param>
        /// <param name="roundUp"></param>
        /// <returns></returns>
        internal static decimal RoundToDivider(decimal value, decimal divider, bool roundUp)
        {
            if (divider <= 0M) return value;                                 // Příklady : (value, divider, roundup)               (21, 5, true)   (-32, 6, true)
            decimal factor = Math.Abs(value) / divider;                      // Podíl value/divider, v absolutní hodnotě           (4.25)          (5.33)
            factor = (roundUp ? Math.Ceiling(factor) : Math.Floor(factor));  // Zaokrouhlit nahoru/dolů, v absolutní hodnotě       (5.00)          (6.00)
            return Math.Sign(value) * factor * divider;                      // Vrátit znaménko, vrátit podíl v násobku dělitele   (25.00)         (-36.00)
        }
        #endregion
        #region CACHOVÁNÍ PARAMETRŮ PROCESU a PARAMETRŮ SKLADOVÉ KARTY PRO ZJEDNODUŠENÍ PRACÍ PŘI PŘÍPRAVĚ NÁVRHŮ
        /// <summary>
        /// Zajistí, že na skladové kartě budou korektně připravené parametry odpovídající parametrům plánovacího procesu a parametrům skladové karty (zkombinuje je).
        /// Pokud už parametry jsou připraveny, nic nedělá.
        /// </summary>
        /// <param name="planningParameters"></param>
        public void CheckStockParams(PlanningParametersCls planningParameters)
        {
            if (this._StockParams == null)
                this._CreateProposalsPrepareParams(planningParameters);
        }
        /// <summary>
        /// Zajistí, že na skladové kartě budou korektně připravené parametry odpovídající parametrům plánovacího procesu a parametrům skladové karty (zkombinuje je).
        /// Pokud už parametry jsou připraveny, přepíše je aktuálními.
        /// </summary>
        /// <param name="planningParameters"></param>
        public void PrepareStockParams(PlanningParametersCls planningParameters)
        {
            this._CreateProposalsPrepareParams(planningParameters);
        }
        /// <summary>
        /// Zkombinuje parametry procesu a stav skladové karty, a vyhodnotí si z toho pracovní proměnné do objektu this._StockParams
        /// Stačí zavolat 1x při zahájení procesu tvorby návrhů.
        /// </summary>
        /// <param name="planningParameters"></param>
        private void _CreateProposalsPrepareParams(PlanningParametersCls planningParameters)
        {
            this._StockParams = _StockParamsCls.Create(this, planningParameters);
        }
        /// <summary>
        /// Uložené akceptované parametry plánovacího procesu kombinované s daty skladové karty.
        /// </summary>
        private _StockParamsCls _StockParams;
        /// <summary>
        /// Privátní třída, obsahující akceptované parametry plánovacího procesu kombinované s daty skladové karty.
        /// </summary>
        private class _StockParamsCls
        {
            /// <summary>
            /// Akceptovat minimum od dodavatele pro běžné návrhy
            /// </summary>
            internal bool AcceptMinDodProposal;
            /// <summary>
            /// Akceptovat minimum od dodavatele pro návrhy MinQty
            /// </summary>
            internal bool AcceptMinDodMinQty;
            /// <summary>
            /// Akceptovat dělitele množství
            /// </summary>
            internal bool AcceptQtyDivider;
            /// <summary>
            /// Akceptovat optimální dávku
            /// </summary>
            internal bool AcceptOptimumBatch;
            /// <summary>
            /// Režim minimální zásoby
            /// </summary>
            internal MinimumStockQtyMode MinStockQtyMode;
            /// <summary>
            /// Minimální zásoba. Pokud se nemá uvažovat, je == 0.
            /// </summary>
            internal decimal MinStockQty;
            /// <summary>
            /// Akceptované Minimum od dodavatele
            /// </summary>
            internal decimal QtyMinDod;
            /// <summary>
            /// Akceptovaný Dělitel příjmu
            /// </summary>
            internal decimal QtyDivider;
            /// <summary>
            /// Akceptovaná Optimální dávka, vstupní ze stavu skladu, nezarovnaná
            /// </summary>
            internal decimal QtyOptimumBatch;
            /// <summary>
            /// Akceptovaná Optimální dávka, zarovnaná podle pravidel (minimum pro dodavatele, dělitel příjmu) pro běžný návrh na příjem
            /// </summary>
            internal decimal QtyOptimumBatchProposal;
            /// <summary>
            /// Akceptovaná Optimální dávka, zarovnaná podle pravidel (minimum pro dodavatele, dělitel příjmu) pro návrh MinQty
            /// </summary>
            internal decimal QtyOptimumBatchMinQty;
            /// <summary>
            /// Akceptovaná Maximální zásoba
            /// </summary>
            internal decimal QtyMaximum;
            /// <summary>
            /// Akceptovaný požadavek Pracovat s kategoriemi
            /// </summary>
            internal bool WorkWithCategories;
            ///<summary>
            ///Časová kumulace návrhů: časový úsek, po který je možno kumulovat požadavky do jednoho zajištění
            ///</summary>
            public TimeSpan CumulationTimePeriod;
            private _StockParamsCls()
            { }
            /// <summary>
            /// Vytvoří a vrátí objekt s daty akceptovanými ze skladové karty a z parametrů procesu
            /// </summary>
            /// <param name="stockCard"></param>
            /// <param name="planningParameters"></param>
            /// <returns></returns>
            internal static _StockParamsCls Create(StockCardCls stockCard, PlanningParametersCls planningParameters)
            {
                _StockParamsCls par = new _StockParamsCls();
                par.AcceptMinDodProposal = planningParameters.AcceptMinimumDodavatel && stockCard.StavSkMinimumDodavatel > 0M;
                par.AcceptMinDodMinQty = planningParameters.MrpMinDodToMinQty && par.AcceptMinDodProposal;
                par.AcceptQtyDivider = planningParameters.AcceptQtyDivider && stockCard.StavSkDelitelPrijem > 0M;
                par.AcceptOptimumBatch = planningParameters.AcceptOptimumBatch && !stockCard.StavSkMaximalniDavka.IsNull && stockCard.StavSkMaximalniDavka.Value > 0M;
                par.MinStockQtyMode = planningParameters.MinStockQtyMode;

                // DAJ 0044303 : Pokud PJ s zdrojů má zakázku, 
                //  tak minimální množství skladu vždy = 0 (nezajišťovat minimum), minimum od dodavatele = 0, dělitel = 0. 
                bool acceptMin = (stockCard.PlanUnitSData == null ? true : stockCard.PlanUnitSData.OrderRelation.IsNull);
                par.MinStockQty = ((acceptMin && par.MinStockQtyMode == MinimumStockQtyMode.OnFirstProposal || par.MinStockQtyMode == MinimumStockQtyMode.OnPlanBegin) ? stockCard.MinStockQty : 0M);
                par.QtyMinDod = (acceptMin ? stockCard.StavSkMinimumDodavatel : 0m);
                par.QtyDivider = ((acceptMin && par.AcceptQtyDivider) ? stockCard.StavSkDelitelPrijem : 0M);
                par.QtyOptimumBatch = (par.AcceptOptimumBatch ? stockCard.StavSkMaximalniDavka.Value : 0M);
                par.QtyOptimumBatchProposal = par.GetQtyAdjustedByStock(par.QtyOptimumBatch, false);
                par.QtyOptimumBatchMinQty = par.GetQtyAdjustedByStock(par.QtyOptimumBatch, true);
                par.QtyMaximum = (stockCard.StavSk.PocetMaximum.IsNull ? 0M : stockCard.StavSk.PocetMaximum.Value);

                par.WorkWithCategories = planningParameters.MrpWorkWithCategories;

                // Agregační perioda (TimeSpan)
                par.CumulationTimePeriod = TimeSpan.Zero;
                if (stockCard.StavSk.CyklusObjednani > 0)
                    par.CumulationTimePeriod = TimeSpan.FromDays(stockCard.StavSk.CyklusObjednani);
                else if (stockCard.StavSk.Dodavka > 0)
                    par.CumulationTimePeriod = TimeSpan.FromDays(stockCard.StavSk.Dodavka);
                else
                    par.CumulationTimePeriod = planningParameters.DefaultAggregationTime;

                return par;
            }
            /// <summary>
            /// Metoda vrátí dané množství ošetřené podle pravidel skladu (kromě dělení na dávky).
            /// Metoda aplikuje minimum od dodavatele a zarovnání pomocí dělitele množství na příjmu.
            /// </summary>
            /// <param name="qty">Požadované množství</param>
            /// <param name="isForMinQty">Zarovnat pro minimální množství (true) / pro běžný návrh (false)</param>
            /// <returns></returns>
            internal decimal GetQtyAdjustedByStock(decimal qty, bool isForMinQty)
            {
                decimal result = qty;

                bool acceptMinDod = (isForMinQty ? this.AcceptMinDodMinQty : this.AcceptMinDodProposal);

                // Úprava množství jedné dávky podle minima od dodavatele:
                if (acceptMinDod && result < this.QtyMinDod)
                    result = this.QtyMinDod;

                // Úprava množství jedné dávky podle dělitele množství na příjmu:
                if (this.AcceptQtyDivider)
                    result = StockCardCls.RoundToDivider(result, this.QtyDivider, true);

                return result;
            }
        }
        #endregion
        #region ZÍSKÁNÍ SEZNAMU GIDŮ ZÁZNAMŮ OSY S, KTERÉ JSOU NAVÁZANÉ NA TÉTO KARTĚ
        /// <summary>
        /// Metoda vrátí soupis klíčů AxisGID pro všechny zdejší záznamy o pohybu na skladové kartě.
        /// Tato metoda nenastavuje příznak AxisBalanceExists.
        /// </summary>
        /// <returns></returns>
        internal List<GID> GetAxisGIdList()
        {
            List<GID> axisGIdList = new List<GID>();
            foreach (StockChangeCls changeItem in this._StockChanges)
                axisGIdList.Add(changeItem.AxisGID);
            return axisGIdList;
        }
        /// <summary>
        /// Metoda vrátí soupis klíčů AxisGID pro všechny zdejší záznamy o pohybu na skladové kartě.
        /// Současně metoda nastaví AxisBalanceExists na false, očekává se že vztahy typu AxisBalance pro tuto skladovou kartu budou smazány.
        /// </summary>
        /// <returns></returns>
        internal List<GID> GetAxisGIdForRemoveBalance()
        {
            this.AxisBalanceExists = false;               // Nastavím příznak na false, i když vztahy ze systému AxisHeap neodebírám (ale nejspíš se odeberou).
            return this.GetAxisGIdList();
        }
        #endregion
        #region LOAD
        /// <summary>
        /// Metoda zajistí finalizaci dat na skladové kartě po jejich načtení z databáze.
        /// Provede setřídění a rekalkulaci dat, a nastaví se StockCardState = StockCardStateType.Balanced a this.AxisBalanceExists = true.
        /// </summary>
        internal void DataLoadEnd()
        {
            this.SortIfNeed();
            this.StockCardState = StockCardStateType.Balanced;
            this.AxisBalanceExists = true;
        }
        #endregion
        #region SAVE
        /// <summary>
        /// Metoda uloží do databáze data této jedné skladové karty.
        /// Ukládá data stavu skladu PJ.
        /// </summary>
        internal void Save()
        {
            this.PlanUnitSSl.Save();
        }
        #endregion
        #region INTERNAL STATIC METODY (Library) - nic
        #endregion
        #region IRecordKey Members
        int IRecordKey.ClassNumber { get { return this.StavSk.ClassNumber; } }
        int IRecordKey.RecordNumber { get { return this.StavSk.RecordNumber; } }
        #endregion
        #region IDisposable Members
        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            _StockChanges.Clear();
        }
        #endregion
    }
    #endregion
    #region CLASS StockChangeCls : jedna položka změny skladové zásoby, spojená se záznamem osy S
    /// <summary>
    /// Třída, která obsahuje jednu položku změny skladové zásoby.
    /// </summary>
    internal class StockChangeCls
    {
        #region KONSTRUKCE
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="axisItem">Data o záznamu osy S</param>
        internal StockChangeCls(MaterialPlanAxisItemCls axisItem)
        {
            this._ChangeDate = axisItem.DatePlanEnd;
            this._PrevQty = 0.0M;
            this._ChangeQty = axisItem.ChangeQty;
            this._RealItem = axisItem.IsRealItem;
            this._MaterialPlanAxisItem = axisItem;
        }
        /// <summary>
        /// Datum změny
        /// </summary>
        private DateTime _ChangeDate;
        /// <summary>
        /// Stav zásob před provedením této změny.
        /// </summary>
        private decimal _PrevQty;
        /// <summary>
        /// Množství na dokladu o změně, zde již se znaménkem, které vyjadřuje směr pohybu (např. snížení výdejem do komponenty má znaménko MINUS).
        /// Tato hodnota se snadno přičítá k průběžnému stavu (předchozí _ResultQty) a určuje tak zdejší _ResultQty.
        /// Důvodem je rychlost přepočtu (při každém přepočtu se nemusí hledat znaménko, to se určí jedenkrát).
        /// </summary>
        private decimal _ChangeQty;
        /// <summary>
        /// Příznak reálného pohybu (true) / plánovaný pohyb (false), slouží pro třídění
        /// </summary>
        private bool _RealItem;
        /// <summary>
        /// Doklad, který změnu způsobil (způsobitel). U skladových dokladů je číslem záznamu číslo objektu.
        /// </summary>
        private MaterialPlanAxisItemCls _MaterialPlanAxisItem;
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "{AxisID=" + this.AxisID + "; Date=" + _ChangeDate.ToString() + "; Type=" + this._MaterialPlanAxisItem.ChangeType + "; ChangeQty=" + _ChangeQty.ToString() + "; Result=" + ResultQty.ToString() + "}";
        }
        #endregion
        #region PUBLIC PROPERTY
        /// <summary> { get; } ID záznamu osy.</summary>
        internal int AxisID { get { if (this.MaterialPlanAxisItem == null) return 0; return this.MaterialPlanAxisItem.AxisID; } }
        /// <summary> { get; } Unikátní GID osy = číslo třídy plus můj primární klíč</summary>
        internal GID AxisGID { get { if (this.MaterialPlanAxisItem == null) return GID.Empty; return this.MaterialPlanAxisItem.AxisGID; } }
        /// <summary> { get; }
        /// Datum změny
        /// </summary>
        internal DateTime ChangeDate { get { return this._ChangeDate; } }
        /// <summary> { get; }
        /// Stav zásob PŘED provedením této změny.
        /// </summary>
        internal decimal PrevQty { get { return this._PrevQty; } }
        /// <summary> { get; }
        /// Množství na dokladu o změně, zde již se znaménkem, které vyjadřuje směr pohybu (např. snížení výdejem do komponenty má znaménko MINUS).
        /// Tato hodnota se snadno přičítá k průběžnému stavu (předchozí _ResultQty) a určuje tak zdejší _ResultQty.
        /// Důvodem je rychlost přepočtu (při každém přepočtu se nemusí hledat znaménko, to se určí jedenkrát).
        /// </summary>
        internal decimal ChangeQty { get { return this._ChangeQty; } }
        /// <summary> { get; }
        /// Stav zásob po provedení této změny.
        /// Pozor: jde o stav, který skutečně započítává množství ChangeQty, bez ohledu na aktivitu záznamu materiálové osy.
        /// Tzn. vyjadřuje, jaká bude zásoba, když by se tento pohyb provedl.
        /// Pokud je tento pohyb aktivní, pak je ResultQty = NextQty.
        /// Pokud je tento pohyb neaktivní, pak NextQty = PrevQty.
        /// </summary>
        internal decimal ResultQty { get { return this._PrevQty + this._ChangeQty; } }
        /// <summary> { get; }
        /// Stav zásob následující po tomto záznamu.
        /// Tato hodnota reaguje na infromaci o skrytí tohoto záznamu (this.IsHidden): 
        /// pokud není záznam skrytý (default), pak FlowQty = PrevQty + ChangeQty
        /// pokud je záznam skrytý (stav = Hidden), pak FlowQty = PrevQty.
        /// </summary>
        internal decimal FlowQty { get { if (this.IsHidden) return this._PrevQty; return this._PrevQty + this._ChangeQty; } }
        /// <summary> { get; }
        /// Příznak, že tento záznam o pohybu je skrytý a nemá vliv na vývoj zásoby na skladové kartě (true).
        /// Default (=false) = záznam se má do stavu na kartě započítávat.
        /// </summary>
        internal bool IsHidden { get { return (this.PlanningState == PlanSAxisPlanningState.Hidden); } }
        /// <summary> { get; }
        /// Stav této věty v procesu plánování.
        /// </summary>
        internal PlanSAxisPlanningState PlanningState { get { if (this.MaterialPlanAxisItem == null) return PlanSAxisPlanningState.NotExists; return this.MaterialPlanAxisItem.PlanningState; } }
        /// <summary> { get; }
        /// Doklad, který změnu způsobil (způsobitel).
        /// </summary>
        internal MaterialPlanAxisItemCls MaterialPlanAxisItem { get { return this._MaterialPlanAxisItem; } }
        #endregion
        #region KOMPARÁTOR
        /// <summary>
        /// Komparátor dvou záznamů, který určuje pořadí záznamů na časové ose.
        /// Nejprve třídí podle data, starší dopředu.
        /// Pak třídí příjmy a výdeje, příjmy dopředu. Za příjem považuje i záporný výdej, a za výdej považuje i záporný příjem.
        /// Pak třídí nejprve reálné položky, a po nich plánované položky.
        /// </summary>
        /// <param name="a">Položka A</param>
        /// <param name="b">Položka B</param>
        /// <returns>Vztah položek A a B</returns>
        public static int CompareByAxisOrder(StockChangeCls a, StockChangeCls b)
        {
            int cmp;
            cmp = a._ChangeDate.CompareTo(b._ChangeDate); ;
            if (cmp != 0) return cmp;

            int aSign = Math.Sign(a._ChangeQty);
            int bSign = Math.Sign(b._ChangeQty);
            cmp = bSign.CompareTo(aSign);        // Vracím opačný výsledek (b compare to a), protože nejdřív chci kladná čísla, a pak záporná
            if (cmp != 0) return cmp;

            aSign = (a._RealItem ? -1 : 1);
            bSign = (b._RealItem ? -1 : 1);
            cmp = aSign.CompareTo(bSign);        // Vracím nejdříve reálné záznamy (sign mají -1), a pak plánované (sign mají +1).
            if (cmp != 0) return cmp;

            // Pokud jsou stejné, pak vyhodnotím pořadí návrhu (ProposalSeq) a v rámci stejného návrhu pak pořadí dávky (ProposalSeq):
            if (a._MaterialPlanAxisItem != null && b._MaterialPlanAxisItem != null)
            {
                cmp = a._MaterialPlanAxisItem.ProposalSeq.CompareTo(b._MaterialPlanAxisItem.ProposalSeq);
                if (cmp != 0) return cmp;

                cmp = a._MaterialPlanAxisItem.ProposalSub.CompareTo(b._MaterialPlanAxisItem.ProposalSub);
                if (cmp != 0) return cmp;
            }
            return 0;
        }
        #endregion
        #region VEŘEJNÉ (INTERNAL) METODY
        /// <summary>
        /// Změní datum položky StockChangeCls (this), ale nemění datum na navázaném záznamu osy S (this._MaterialPlanAxisItem).
        /// Důvod: v tomto místě není zřejmé, kdo je původcem změny, zda proces karty, nebo proces osy.
        /// Nechť si tedy původce zajistí změnu na ose S podle svého uvážení.
        /// Změna na ose může být prováděna zpětně (End = ToHistory = Begin) nebo naopak, a to zde nedokážeme rozhodovat.
        /// Navíc právě určení toho intervalu (Begin-End na ose S) je nad rámec zdejších možností.
        /// </summary>
        /// <param name="newChangeDate"></param>
        internal void SetChangeDate(DateTime newChangeDate)
        {
            this._ChangeDate = newChangeDate;
            // this._MaterialPlanAxisItem.SetAxisDateEnd(newChangeDate);
        }
        /// <summary>
        /// Zapíše si výchozí množství před tímto pohybem.
        /// Tím dojde ke správnému určení množství zůstávájícímu po tomto pohybu (ResultQty a FlowQty).
        /// Tato metoda rovnou vrátí ResultQty po provedené změně.
        /// </summary>
        /// <param name="prevQty"></param>
        /// <returns></returns>
        internal decimal RecalcResultQty(decimal prevQty)
        {
            this._PrevQty = prevQty;
            return this.ResultQty;
        }
        /// <summary>
        /// Zapíše si výchozí množství před tímto pohybem.
        /// Tím dojde ke správnému určení množství zůstávájícímu po tomto pohybu (ResultQty a FlowQty).
        /// Tato metoda rovnou vrátí FlowQty po provedené změně.
        /// </summary>
        /// <param name="prevQty"></param>
        /// <returns></returns>
        internal decimal RecalcFlowQty(decimal prevQty)
        {
            this._PrevQty = prevQty;
            return this.FlowQty;
        }
        #endregion
    }
    /// <summary>
    /// Třída, která umožňuje snadno provádět kalkulaci průběžného stavu zásob podle vývoje, jak jej popisuje třída StockChangeCls.
    /// Jinými slovy: v objektu této třídy (StockChangeFlowSummaryCls) se kalkuluje průběžný stav zásoby, v metodě StockChangeCls.CalcFlowQty(ref StockChangeFlowSummaryCls).
    /// </summary>
    internal class StockChangeFlowSummaryCls
    {
        internal StockChangeFlowSummaryCls(decimal initialQty)
        {
            Sequence = 0;
            FlowQtyDisp = initialQty;
            FlowQtyProp = initialQty;
            FlowQtyProp2 = initialQty;
            DateFirstProposal = SqlDateTime.Null;
        }
        internal StockChangeFlowSummaryCls(IEnumerable<StockCardCls> stockCards)
        {
            Sequence = 0;
            decimal initialQty = 0M;
            if (stockCards != null)
            {
                foreach (StockCardCls stockCard in stockCards)
                    initialQty += stockCard.InitialStockQty;
            }
            FlowQtyDisp = initialQty;
            FlowQtyProp = initialQty;
            FlowQtyProp2 = initialQty;
            DateFirstProposal = SqlDateTime.Null;
        }
        /// <summary>
        /// Pořadové číslo změny, na počátku je 0
        /// </summary>
        internal int Sequence;
        /// <summary>
        /// Stav k dispozici reálně
        /// </summary>
        internal decimal FlowQtyDisp;
        /// <summary>
        /// Stav k dispozici včetně všech návrhů (na příjem, na výdej)
        /// </summary>
        internal decimal FlowQtyProp;
        /// <summary>
        /// Stav k dispozici včetně návrhů na výdej
        /// </summary>
        internal decimal FlowQtyProp2;
        /// <summary>
        /// Datum prvního návrhu na příjem
        /// </summary>
        internal SqlDateTime DateFirstProposal;
        /// <summary>
        /// Z daného záznamu osy S do sebe přičte změnu stavu.
        /// </summary>
        /// <param name="axisS"></param>
        internal void GetFromAxis(MaterialPlanAxisItemCls axisS)
        {
            // Increment sequence:
            this.Sequence++;

            // Množství z osy, podle podmínek napočíst do flowQty:
            decimal changeQty = axisS.ChangeQty;
            bool isReal = axisS.IsRealItem;
            bool isRqst = axisS.IsQtyRequest;
            if (isRqst)
                this.FlowQtyDisp += changeQty;           // Množství po této změně, jen reálné záznamy
            this.FlowQtyProp += changeQty;               // Množství po této změně, všechny záznamy (i návrhy na příjem a výdej)
            if (isRqst || changeQty < 0M)
                this.FlowQtyProp2 += changeQty;          // Množství po této změně, reálné záznamy a návrhy na výdej

            // Datum prvního návrhu na příjem:
            if (DateFirstProposal.IsNull && axisS.IsProposal && axisS.ChangeQty > 0M)
                DateFirstProposal = new SqlDateTime(axisS.DatePlanEnd);
        }
        /// <summary>
        /// Ze sebe do záznamu osy S uloží průběžné výsledky, do kolonek typu Stock
        /// </summary>
        /// <param name="axisS"></param>
        internal void SetToAxisSStock(MaterialPlanAxisItemCls axisS)
        {
            // Průběžná data vložit do záznamu osy:
            axisS.AxisSeq2 = this.Sequence;
            axisS.QtyStD = this.FlowQtyDisp;
            axisS.QtyStDp = this.FlowQtyProp;
            axisS.QtyStDp2 = this.FlowQtyProp2;
        }
        /// <summary>
        /// Ze sebe do záznamu osy S uloží průběžné výsledky, do kolonek typu Unit
        /// </summary>
        /// <param name="axisS"></param>
        internal void SetToAxisSUnit(MaterialPlanAxisItemCls axisS)
        {
            // Průběžná data vložit do záznamu osy:
            axisS.AxisSeq = this.Sequence;
            axisS.QtyD = this.FlowQtyDisp;
            axisS.QtyDp = this.FlowQtyProp;
            axisS.QtyDpC = this.FlowQtyProp2;
        }
    }
    #endregion
}
